POJ1185状态压缩DP
难得的中文题.
POJ1185http://poj.org/problem?id=1185
方法就是用DP[i][r][p]表示第i行状态为r,第i-1行状态是p时的最多个数。而这里p受到r的限制,而第i-2行状态q则受到r和p两个状态限制。状态转移方程就是:
DP[i][r][p] = MAX{DP[i-1][p][q] +num[r]}
其中,p是受到r的限制时枚举的状态,q是受到r和p共同限制时候的状态,num[r]表示状态r里面的布局炮兵所摆的个数。
这里我们可以看到就要枚举i,r,p,q,这4 个变量,i的范围是100,而其他几个则都是1<<10,复杂度颇为偏高。而实际上由于每一行里面有很多都是某些位置被其他位置影响的。比如: 1110001, 如果第一个位置放上炮兵,那么第二第三的位置都会受到影响,而一个也放不了。
解决方案就是不去管那些相互有影响的状态,把形如:
1000000 0100000 ... ...
1001000 0100001 ... ...
...
1001001
这些相互之间没有影响的状态找出来,这样所有的状态数就会减少至少于60种(我算了一下,好像是58种),这样一来就是60*60*60*100,可以过了。
1 #include <stdio.h> 2 #include <string.h> 3 #define mem(a) memset(a,0,sizeof(a)) 4 #define MAX(a,b) ((a) > (b) ? (a) : (b)) 5 6 int DP[2][60][60],num1[60],s[60],row[105]; 7 int N,M,StateNum;//StateNum记录不会产生冲突的状态数目 8 9 bool Judge(int x)//判断状态x是否存在有相邻或相隔为2的1,没有返回true 10 { 11 if(x & (x<<1)) return false; 12 if(x & (x<<2)) return false; 13 return true; 14 } 15 16 int NumOf1(int x)//统计x里面1的个数 17 { 18 int num = 0; 19 while(x) { 20 num++; 21 x &= ~(-x); 22 } 23 return num; 24 } 25 26 void Get_Num1_s()//给num1数组和s数组赋值 27 { 28 StateNum = 0; 29 for(int i = 0 ; i < (1<<M) ; i ++ ) if(Judge(i))//从所有的状态中找出不会产生冲突的状态 30 { 31 s[StateNum] = i;//记录这些状态 32 num1[StateNum] = NumOf1(i);//和这些状态1的个数 33 StateNum ++ ; 34 } 35 } 36 37 int main() 38 { 39 while(~scanf("%d%d%*c", &N, &M)) 40 { 41 char str[15]; 42 memset(DP,-1,sizeof(DP)); 43 for(int i=1;i<=N;i++) 44 { 45 scanf("%s", str); 46 for(int j=0;j<M;j++) 47 { 48 if(str[j]=='P') row[i] |= (1<<j);//row记录每一行可行状态 49 } 50 } 51 52 Get_Num1_s(); 53 54 for(int i=0;i<StateNum;i++) if( !(s[i] & ~row[1]) )//给第一组状态赋初值 55 { 56 DP[0][i][0] = num1[i]; 57 } 58 int key = 0; 59 for(int i=2;i<=N;i++)//枚举每一行 60 { 61 key = !key; 62 for(int r=0;r<StateNum;r++) if( !(s[r] & ~row[i]) )//枚举当前行的状态,且属于当前行里 63 { 64 for(int p=0;p<StateNum;p++) if( !(s[p] & s[r]) )//枚举上一行的状态,且和当前行没有冲突 65 { 66 for(int q=0;q<StateNum;q++) if( !(s[q] & (s[r]|s[p])) )//枚举上上行,且与上两行没有冲突 67 { 68 if(DP[!key][p][q] != -1)//必须是合法的状态才可以被记录在内 69 { 70 DP[key][r][p] = MAX(DP[key][r][p], DP[!key][p][q]+num1[r]); 71 } 72 } 73 } 74 } 75 } 76 int ans = 0; 77 for(int i=0;i<StateNum;i++) 78 { 79 for(int j=0;j<StateNum;j++) 80 { 81 ans = MAX(ans, DP[key][i][j]); 82 } 83 } 84 printf("%d\n", ans); 85 } 86 return 0; 87 }