HDU2167 Pebbles(状压DP)
题目给一张n×n的格子,每个格子都有数字,要从格子中取若干个数字,八个方向相邻的数字不能一起取,问取的数字最大和是多少。
从第一行一行一行看下去,可以发现第1行取哪几列只会影响到第2行,第3行后面的一点影响都没有。即第i行的决策只受i-1行决策的影响。
那么自然想到状态DP——
- dp[i][S]前i行其中第i行取的列的集合是S的取数最大和
- dp[i][S]=max(dp[i-1][S'])+集合S数字和(S是S'的合法的下一行的取法)
虽然题目n最多15,集合S就215种状态,但事实上合法的(不能同时取同一行相邻的两列数字)集合S状态只有1000多个。枚举即可转移,为了保证时间复杂度做一些预处理就行了。
1 #include<cstdio> 2 #include<cstring> 3 #include<vector> 4 #include<algorithm> 5 using namespace std; 6 int read(char *&s){ 7 int res=-1; 8 sscanf(s,"%d",&res); 9 while(*s==' ') ++s; 10 while(*s>='0' && *s<='9') ++s; 11 while(*s==' ') ++s; 12 return res; 13 } 14 int n,a[15][15],d[15][1<<15],sta[1<<15],sn; 15 bool isok(int s){ 16 for(int i=1; i<n; ++i){ 17 if(((s>>i-1)&1) && ((s>>i)&1)) return 0; 18 } 19 return 1; 20 } 21 bool isok(int x,int y){ 22 for(int i=0; i<n; ++i){ 23 if(((x>>i)&1)==0) continue; 24 if((y>>i)&1) return 0; 25 if(i>0 && ((y>>i-1)&1)) return 0; 26 if(i<n-1 && ((y>>i+1)&1)) return 0; 27 } 28 return 1; 29 } 30 int main(){ 31 char str[1111]; 32 while(gets(str) && *str){ 33 n=0; 34 char *s=str; int t; 35 while(t=read(s),t!=-1) a[0][n++]=t; 36 for(int i=1; i<n; ++i){ 37 gets(str); s=str; 38 for(int j=0; j<n; ++j) a[i][j]=read(s); 39 } 40 sn=0; 41 for(int i=0; i<(1<<n); ++i){ 42 if(isok(i)) sta[sn++]=i; 43 } 44 memset(d,0,sizeof(d)); 45 for(int i=0; i<sn; ++i){ 46 for(int j=0; j<n; ++j){ 47 if((sta[i]>>j)&1) d[0][sta[i]]+=a[0][j]; 48 } 49 } 50 vector<int> vec[2222]; 51 for(int i=0; i<sn; ++i){ 52 for(int j=0; j<sn; ++j){ 53 if(isok(sta[i],sta[j])) vec[i].push_back(j); 54 } 55 } 56 for(int i=1; i<n; ++i){ 57 for(int j=0; j<sn; ++j){ 58 for(int k=0; k!=vec[j].size(); ++k) d[i][sta[j]]=max(d[i][sta[j]],d[i-1][sta[vec[j][k]]]); 59 for(int k=0; k<n; ++k){ 60 if((sta[j]>>k)&1) d[i][sta[j]]+=a[i][k]; 61 } 62 } 63 } 64 int res=0; 65 for(int i=0; i<sn; ++i){ 66 res=max(res,d[n-1][sta[i]]); 67 } 68 printf("%d\n",res); 69 getchar(); 70 } 71 return 0; 72 }