HDU1565—方格取数(1)——状压DP

Problem Description

给你一个n*n的格子的棋盘,每个格子里面有一个非负数。
从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取的数所在的2个格子不能相邻,并且取出的数的和最大。

Input

包括多个测试实例,每个测试实例包括一个整数n 和n*n个非负数(n<=20)

Output

对于每个测试实例,输出可能取得的最大的和

Sample Input
3
75 15 21
75 15 28
34 70 5
Sample Output
188

本题思路::肯定是状态压缩的DP。把每一种可能的状态存放在 mp 数组中,共有tot种状态。用 dp 二维数组记录最大值。其中dp[ i ][ j ]表示到第 i 行 j 状态的最大值。 状态转变方程为dp[ i ][ j ] = max(dp[ i ][ j ] , dp[ i-1 ][ k ] + sum[ j ]); 其中dp[ i - 1 ][ k ]表示第  i - 1 行 k  状态  ,sum 为 i 行 j 状态的总和。详情看代码。
//这道题的关键在于时间和空间复杂度思路还是比较简单
//第一次::把每一个状态都记录在数组中,超了空间
//第二次::把不可能的状态踢掉,超了时间
//第三次::看了网上的题解,发现可以把当前行,和上一行换位置,节省了大量时间,过掉了
#include <iostream>
#include <sstream>
#include <ios>
#include <iomanip>
#include <functional>
#include <algorithm>
#include <vector>
#include <string>
#include <list>
#include <queue>
#include <deque>
#include <stack>
#include <set>
#include <map>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <climits>
#include <cctype>
using namespace std;
#define XINF INT_MAX
#define INF 0x3FFFFFFF
#define MP(X,Y) make_pair(X,Y)
#define PB(X) push_back(X)
#define REP(X,N) for(int X=0;X<N;X++)
#define REP2(X,L,R) for(int X=L;X<=R;X++)
#define DEP(X,R,L) for(int X=R;X>=L;X--)
#define CLR(A,X) memset(A,X,sizeof(A))
#define IT iterator
typedef long long ll;
typedef pair<int,int> PII;
typedef vector<PII> VII;
typedef vector<int> VI;
//const int MAXN = 10010;
//#define INF 0x3FFFFFFF
/***************************************
*****************头文件*****************
****************************************/
int mp[20000];   //曾经尝试用map,后来发现和数组功能完全相同
int dp[20][20000];//可以进一步改为滚动数组可节省空间
int maps[23][23];//记录值
int main()
{
	int n;
	while(cin>>n){

		memset(dp,0,sizeof(dp));
		REP2(i,1,n)//这里从第一行开始记录
		REP(j,n)	//这里从第零列开始记录
		{
		    cin>>maps[i][j];
		}
		int tot = 0;//tot用来记录有多少种可能性
		for(int i =0;i<(1<<n);i++)//遍历每一种情况
		{
			if(i & (i << 1)) continue; //出现相邻直接过掉
			mp[tot++] =i;
		}
		int ans = 0;//用来记录结果
		for(int i = 0; i < n; i++){//遍历每一行因为从0开始,所以下面为i+1
			for(int j = 0; j < tot; j++){ //遍历当前行的每一种状态
			    int sum = 0;					//用来记录当前状态第i+1行的总和
			    REP(l,n){
                        int t = 1<<l;
                        if(mp[j]&t)
                            sum += maps[i+1][l];
					}
				for(int k = 0; k < tot; k++){ //遍历上一行状态
					if(mp[j] & mp[k]) continue; //两行之间有相邻
					dp[i+1][j] = max(dp[i+1][j],sum+dp[i][k]);//截止到i+1行为j状态是的最大值
						ans = max(ans,dp[i+1][j]);//答案应取每一个最大状态的最大值
				}
			}
		}
		cout<<ans<<endl;
	}
	return 0;
}




你可能感兴趣的:(题解,dp,HDU,状态压缩,方格取数1)