以前没做过这种类型的状态压缩DP,刚开始看着没想明白,后来跟着代码看着好多了。
题目:poj1185炮兵阵地
题意:一个矩阵,有一些地方可以放大炮,有的地方不能放,大炮对上下左右的攻击范围都是两格。两个大炮不能互相攻击到,问最多放多少大炮。
思路:求出每一行的所有状态数,即用二进制表示的所有可行状态(1010,选第一个和第三个)。大炮的攻击距离是2,所以本行、上一行、上上一行不能有大炮在同一行。遍历本行、上一行、上上一行,计算并记下可行状态的结果。
代码:
#include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <iostream> #include <algorithm> #include <queue> #include <stack> #include <vector> #include <set> #include <string> using namespace std; #define For(i,a) for(i=0;i<a;i++) #define Foru(i,a,b) for(i=a;i<=b;i++) #define Ford(i,a,b) for(i=a;i>=b;i--) #define PB push_back #define INF 0x7fffffff int n, m; int cnt[120]; int now[102],last[102],lastlast[102]; int dp[120][120], lastdp[120][120]; char g[102][12]; int nowsize, lastsize, lastlastsize; void init(int i, int j, int p, int sum){ if( j > m) { now[nowsize] = p; cnt[nowsize++] = sum; return ;} if( g[i][j] == 'P' ) init(i, j+3, p|1<<j, sum+1); init(i, j+1, p, sum); } void DP(){ for(int i = 0; i < n; i ++){ nowsize = 0; init(i,0,0,0); //cout << nowsize << endl; memset(dp, 0, sizeof(dp)); for(int j = 0; j < nowsize; j ++) { for(int k = 0; k < lastsize; k ++){ for(int t = 0; t < lastlastsize; t ++){ if( now[j] & last[k] || now[j] & lastlast[t] || last[k] & lastlast[t]) continue; dp[j][k] = max(dp[j][k], lastdp[k][t] + cnt[j] ); // lastdp[j][k] = dp[j][k]; } } } int k, j; For(k, nowsize) For(j, lastsize) lastdp[k][j] = dp[k][j]; for(int j = 0; j < lastsize; j ++) lastlast[j] = last[j]; lastlastsize = lastsize; for(int j = 0; j < nowsize; j ++) last[j] = now[j]; lastsize = nowsize; //for(int j = 0; j < nowsize; j ++) lastdp[ } } int main(){ while( ~scanf("%d%d",&n,&m)){ for(int i = 0; i < n; i ++) scanf("%s",g[i]); //memset(lastdp, 0, sizeof(lastdp)); last[0]=lastlast[0]=lastdp[0][0]=0; lastsize = lastlastsize = 1; DP(); int ans = 0; for(int i = 0; i < lastsize; i ++){ for(int j = 0; j < lastlastsize; j ++){ ans = max( ans, lastdp[i][j]); } } cout << ans << endl; } return 0; }
题目:ACdream Chess
题意:有一个棋盘,棋盘上有些点能放棋子,有些点不可以。一个棋子能够控制相邻的四个位置,两个棋子不能互相控制。求最少多少个棋子能控制全盘?
思路:先学的上面的,根据上面的思路卡在了控制全盘。需要在上面的代码上加入控制的状态,nowvis[i][j] 本行选i上一行选j本行能控制的格子状态。now[i]和last[j]计算的时候上面一行要全部控制到,因为在下面一行不可能控制到last[j]行。最后求全控制的最小值。群里面大神用dancing links 做的,正在学习中……
代码:
#include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <iostream> #include <algorithm> #include <queue> #include <stack> #include <vector> #include <set> #include <string> using namespace std; #define For(i,a) for(i=0;i<a;i++) #define Foru(i,a,b) for(i=a;i<=b;i++) #define Ford(i,a,b) for(i=a;i>=b;i--) #define PB push_back #define INF 0x7fffffff int ans; int n, m, all; bool g[10][10]; int last[100],now[100]; int lastsize, nowsize, lastlastsize; int cnt[100],dp[110][110],lastdp[100][110]; int bit[10],nowvis[110][110],lastvis[110][110]; int getbit(int p){ int i = 0, res = 0; while(p){ bit[i++] = p%2; p /= 2; } if( bit[0] == 1) res |= 3; for(int j = 1; j < i; j ++) if(bit[j] == 1) res |= 7<<j-1; if(res > all) res = res & (res & all); return res; } void init(int i, int j, int p, int sum){ if( j >= n) { now[nowsize] = p; cnt[nowsize++] = sum; return ;} if( g[i][j] == 0 ) init(i, j+2, p|1<<j, sum+1); init(i, j+1, p, sum); } void DP(){ for(int i = 0; i < n; i ++){ nowsize = 0; memset(nowvis,0,sizeof(nowvis)); memset(bit,0,sizeof(bit)); memset(dp, 0x3f, sizeof(dp)); init(i, 0, 0, 0); for(int j = 0; j < nowsize; j ++){ for(int k = 0; k < lastsize; k ++){ for(int t = 0; t < lastlastsize; t ++){ if( now[j] & last[k] ) continue; if((lastvis[k][t] | now[j] )== all) { dp[j][k] = min(dp[j][k], lastdp[k][t] + cnt[j]); nowvis[j][k] = getbit(now[j]) | last[k]; } } } } //for(int j = 0; j < nowsize; j ++) {for(int k = 0; k < lastsize; k ++) cout << dp[j][k] << ' ' ;cout <<endl;}cout << endl << endl ; lastlastsize = lastsize; for(int j = 0; j < nowsize; j ++) last[j] = now[j]; for(int j = 0; j < nowsize; j ++) for(int k = 0; k < lastsize; k ++) lastdp[j][k] = dp[j][k]; for(int j = 0; j < nowsize; j ++) for(int k = 0; k < lastsize; k ++) lastvis[j][k] = nowvis[j][k]; lastsize = nowsize; } } int main(){ int x, y; while(cin >> n >> m){ all = 1<< n; ans = 0x7fffffff; all --; memset(g,0,sizeof(g)); for(int i = 0; i < m; i ++) { cin >> x >> y; g[x-1][y-1] = 1; } last[0] = dp[0][0] = lastdp[0][0] = 0; lastvis[0][0] = all; lastsize = lastlastsize = 1; DP(); for(int i = 0; i < nowsize; i ++){ for(int j = 0; j < lastsize; j ++){ if(lastvis[i][j] == all) ans = min(dp[i][j], ans); } } if( ans > 100) cout << -1 << endl; else cout << ans << endl; } return 0; }