poj1321 http://poj.org/problem?id=1321
我们可以把棋盘的每一行看做是一个状态,如果某一列放置了棋子,那么就标记为1,否则就标记为0.然后把它看成是一个二进制数,然后转为10进制数,就可以当做数组下标然后进行状态转移了
设dp[i][s] 为处理到第i行时,状态为s的方法数
那么我们枚举第i-1行的所有状态s
dp[i][s] += dp[i-1][s]; //表示第i行不放置棋子的方法数
dp[i][s|(1<<j)] += dp[i-1][s] //表示第i行第j列放置棋子的方法数 (前提是 chess[i][j]=='#' && 状态s第j列没有放过棋子)
1 #include <stdio.h> 2 #include <string.h> 3 char chess[9][9];int dp[11][1<<8]; 4 int n,k; 5 int main() 6 { 7 int i,j,s,ss; 8 while(scanf("%d%d",&n,&k)!=EOF) 9 { 10 int ans = 0; 11 if(n==-1) return 0; 12 memset(dp,0,sizeof(dp)); 13 for(i=1; i<=n; ++i) 14 { 15 scanf("%s",chess[i]); 16 } 17 dp[0][0] = 1; 18 for(i=1; i<=n; ++i) 19 for(s=0; s<(1<<n); ++s) 20 { 21 22 for(j=0; j<n; ++j) 23 if(chess[i][j]=='#'&&((1<<j)&s)==0) 24 dp[i][s|1<<j] += dp[i-1][s];//表示第i行第j列放置棋子的方法数 25 dp[i][s] += dp[i-1][s]; //表示第i行不放置棋子的方法数 26 } 27 ///for(j=1; j<=n; ++j) 28 for(s=0; s<(1<<n); ++s) 29 { 30 i = s; 31 int cnt = 0 ; 32 for(;i;i-= i&-i)//统计状态s有多少个1,表示放了多少个棋子 33 cnt ++; 34 if(cnt==k) 35 ans+= dp[n][s]; 36 } 37 printf("%d\n",ans); 38 } 39 return 0; 40 }
poj3254 http://poj.org/problem?id=3254
本来这一题也想用上面那题的思路做,可是后来发现不行,因为上面那题一行只选择一个位置,而这题一行可以选择多个位置。
那么我们这题可以枚举多个位置,如果和上一行的状态不冲突,那么上一行的状态就能转移到这一行
1 #include <stdio.h> 2 #include <string.h> 3 4 int field[13]; 5 int situation[13]; 6 int dp[13][1<<13]; 7 const int MOD = 100000000; 8 bool isOk(int s) 9 { 10 if(s&(s>>1)) return false; 11 return true; 12 } 13 14 int main() 15 { 16 int n,m,i,j,s,ss,cnt,ans; 17 while(scanf("%d%d",&n,&m)!=EOF) 18 { 19 ans = cnt = 0; 20 memset(dp,0,sizeof(dp)); 21 memset(field,0,sizeof(field)); 22 for(s=0; s<(1<<m); ++s) if(isOk(s)) situation[cnt++] = s;//记录所有可行的状态,即不能有相邻的1 23 for(i=0; i<n; ++i) 24 for(j=0; j<m; ++j) 25 { 26 scanf("%d",&s); 27 field[i] |= (!s)<<j;//这里这样存的含义是,如果situation[] & field[] 不为0,说明状态situation不行 28 } 29 30 //dp[i][j] 表示处理完前i行,第j个状态的方法数 31 for(i=0; i<cnt; ++i) 32 if( !(situation[i] & field[0])) 33 dp[0][i] = 1; 34 35 for(i=1; i<n; ++i) 36 for(s=0; s<cnt; ++s) 37 { 38 if(situation[s]&field[i]) continue;//这一行的状态要合法 39 for(ss=0; ss<cnt; ++ss) 40 { 41 if(situation[ss] & situation[s]) continue;//这一行的状态和上一行的状态不合法 42 dp[i][s] += dp[i-1][ss];//只要上一行的状态ss和这一行的状态s不冲突,那么就能转化为状态s 43 dp[i][s] %= MOD; 44 } 45 } 46 for(i=0; i<cnt; ++i) 47 ans = (ans + dp[n-1][i]) % MOD; 48 printf("%d\n",ans); 49 50 } 51 return 0; 52 }
poj1185 http://poj.org/problem?id=1185
和上面那题差不多,只是当前行的状态不止和上一行有关,还和上上一行有关,所以数组要多开一维
奇怪的是为什么用scanf("%c");G++ac,C++wa 用scanf("%s")G++和C++都wa
1 /* 2 分析:一行的状态不能有 11(连续的两个1) 也能有101(隔一个空然后出现一个1) 3 dp[i][j][k] 表示第i行状态为j第i-1行状态为k时,最多有多少个炮兵摆放 4 dp[i][j][k] = max(dp[i][j][k],dp[i-1][k][t]+num[j]); 5 */ 6 7 #include <stdio.h> 8 #include <string.h> 9 int situation[11]; 10 char matrix[111][11]; 11 int map[111]; 12 int num[111]; 13 int dp[111][100][100]; 14 bool isOk(int s) 15 { 16 if(s&(s<<1)) return false; 17 if(s&(s<<2)) return false; 18 return true; 19 } 20 21 int count(int s) 22 { 23 int ret = 0; 24 for(;s; s-= (s&-s))//统计状态s有多少个1 25 ret++; 26 return ret; 27 } 28 inline int max(const int &a, const int &b) 29 { 30 return a < b ? b : a; 31 } 32 int main() 33 { 34 int n,m,i,j,k,s,cnt,ans,t; 35 char ch; 36 while(scanf("%d%d",&n,&m)!=EOF) 37 { 38 cnt = ans = 0; 39 memset(dp,-1,sizeof(dp)); 40 for(s=0; s<(1<<m); ++s) if(isOk(s)) 41 { 42 num[cnt] = count(s); 43 situation[cnt++] = s; 44 } 45 46 for(i=0; i<n; ++i) 47 { 48 49 getchar(); 50 map[i] = 0; 51 for(j=0; j<m; ++j) 52 { 53 scanf("%c",&ch); 54 if(ch=='H') map[i] |= 1<<j; 55 } 56 } 57 for(i=0; i<cnt; ++i) 58 if(!(situation[i] & map[0])) 59 dp[0][i][0] = num[i]; 60 for(i=1; i<n; ++i) 61 for(j=0; j<cnt; ++j) 62 { 63 if(situation[j] & map[i]) continue; 64 for(k=0; k<cnt; ++k) 65 { 66 if(situation[j] & situation[k]) continue; 67 for(t=0 ;t<cnt; ++t) 68 { 69 if(situation[j] & situation[t]) continue; 70 if(dp[i-1][k][t]==-1) continue; 71 dp[i][j][k] = max(dp[i][j][k],dp[i-1][k][t] + num[j]); 72 } 73 } 74 } 75 for(i=0; i<n; ++i) 76 for(j=0; j<cnt; ++j) 77 for(k=0; k<cnt; ++k) 78 ans = max(ans,dp[i][j][k]); 79 printf("%d\n",ans); 80 } 81 82 return 0; 83 }
hdu3001 http://acm.hdu.edu.cn/showproblem.php?pid=3001
平常的状态压缩,都是某个位置放(1)或者不放(0),所以可以直接用二进制进行压缩
但是这题,每个点可以走两次,那么就可以标记为0,1,2 所以要用三进制进行压缩。
即用一个数组存储每个状态的三进制的每位
1 #include <stdio.h> 2 #include <string.h> 3 const int INF = 1<<30; 4 int three[11],situation[59049][11],edge[11][11],dp[59049][11]; 5 void init() 6 { 7 int i,t,cnt; 8 three[0] = 1; 9 for(i=1; i<=10; ++i) 10 three[i] = three[i-1] * 3; 11 for(i=0; i<59049; ++i) 12 { 13 t = i; 14 cnt = 0; 15 while(t)//存储状态的三进制的每一位 16 { 17 situation[i][cnt++] = t % 3; 18 t /= 3; 19 } 20 } 21 } 22 inline int min(const int &a, const int &b) 23 { 24 return a < b ? a : b; 25 } 26 int main() 27 { 28 init(); 29 int n,m,i,j,a,b,c,ans,s; 30 while(scanf("%d%d",&n,&m)!=EOF) 31 { 32 33 for(i=0; i<n; ++i) 34 for(j=0; j<n; ++j) 35 edge[i][j] = INF; 36 for(i=0; i<n; ++i) 37 for(s=0; s<three[n]; ++s) 38 dp[s][i] = INF; 39 ans = INF; 40 for(i=0; i<m; ++i) 41 { 42 scanf("%d%d%d",&a,&b,&c); 43 a--,b--; 44 if(edge[a][b] > c) 45 edge[a][b] = edge[b][a] = c; 46 } 47 for(i=0; i<n; ++i) dp[three[i]][i] = 0;//处于源点的距离为0 48 //因为走过某些城市可以是任意组合的,所以枚举这些状态 49 for(s=0; s<three[n]; ++s) 50 { 51 bool flag = true; 52 for(i=0; i<n; ++i) 53 { 54 if(situation[s][i]==0) flag = false;//状态s的第i位为0,说明没有走过所有的城市 55 if(dp[s][i]==INF) continue; 56 for(j=0; j<n; ++j) 57 { 58 if(i==j) continue; 59 if(edge[i][j]==INF || situation[s][j]==2) continue; 60 int nextS = s + three[j]; 61 dp[nextS][j] = min(dp[nextS][j],dp[s][i] + edge[i][j]); 62 } 63 } 64 if(flag)//走过所有的状态 65 { 66 for(i=0; i<n; ++i) 67 ans = min(ans,dp[s][i]); 68 } 69 } 70 if(ans == INF) 71 printf("-1\n"); 72 else 73 printf("%d\n",ans); 74 } 75 return 0; 76 }