题意:给出一张n*m的地图,'H'表示高地,不能部署炮兵,'P'表示平原,可以部署炮兵,炮兵之间必须保持横向、纵向至少2个格子的距离,保证没有误伤。问最多可以部署多少炮兵。
分析:
1.可以用一个32位整数存每一行的状态(二进制上1表示有布置炮兵,0表示没有布置炮兵),由于每一行的状态都要前两行的状态来决定,因此真正的一个状态应该包含本行的状态和上一行的状态,用dp[x][i][j]表示第x行上状态i,x-1行状态为j的最优解。
2.每一行的状态其实只有60种,可以直接暴力搜索出来,把这60种状态按二进制递增顺序(排序目的是方便确定状态没有超出n*m的范围)存到数组state[]中,计算好每种状态中能部署炮兵的数目,存到get[]数组。
3.地图转换成一个二进制的数组,即一个整数表示一行的地形(1表示高地,0表示平原)。因此一个状态能否存在的一个条件是state[i] & map[x] == 0,这样是合法的.
4.状态转移方程为dp[x][i][j] = max(dp[x - 1][j][k] + get[i],dp[x][i][j]),条件是state[i] & map[x] == 0 ,state[i] & state[j] == 0,state[i] & state[k] == 0,而且存在dp[x - 1][j][k]。把不存在的dp[x][i][j]标为-1.
PS:看了discuss发现还可以用最大点独立集来做。
#include <cstdio> #include <algorithm> using std::sort; int n,m,state[60],get[60],ts = 0; void search_bruce(int bgn,int s) { if(bgn == 10) { state[ts++] = s; return; } for(int i = 0;i < 2;i++) { if(i == 1 && bgn > 0 && (((1 << (bgn - 1))) & s) != 0) continue; if(i == 1 && bgn > 1 && (((1 << (bgn - 2))) & s) != 0) continue; search_bruce(bgn + 1,( i << bgn) | s); } return; } void counter() { for(int i = 0;i < 60;i++) { get[i] = 0; for(int j = 0;j < 10;j++) { if((state[i] & (1 << j)) != 0) get[i]++; } } } bool conflict(int a,int b) { if((a & b) == 0) return false; else return true; } int main() { search_bruce(0,0); sort(state,state + ts); counter(); int n,m,map[100],dp[2][60][60],td; while(~scanf("%d%d",&n,&m)) { for(int i = 0;i < n;i++) map[i] = 0; for(int i = 0;i < n;i++) { getchar(); for(int j = 0;j < m;j++) { char c; scanf("%c",&c); if(c == 'H') map[i] = (map[i] | (1 << j)); } } int maximum = 1 << m; for(int i = 0;i < 60 && state[i] < maximum;i++) { for(int j = 0;j < 60 && state[j] < maximum;j++) { dp[0][i][j] = -1; } } dp[0][0][0] = 0; for(td = 1;td <= n;td++) { int now = td % 2,before = (td + 1) % 2; for(int i = 0;i < 60 && state[i] < maximum;i++) { for(int j = 0;j < 60 && state[j] < maximum;j++) { if(conflict(state[i],map[td - 1]) || conflict(state[i],state[j])) { dp[now][i][j] = -1; continue; } dp[now][i][j] = 0; for(int k = 0;k < 60 && state[k] < maximum;k++) { if(conflict(state[i],state[k]) || dp[before][j][k] == -1) dp[now][i][j] = std::max(dp[now][i][j],get[i]); else dp[now][i][j] = std::max(dp[now][i][j],dp[before][j][k] + get[i]); } } } } int max = 0; td = (td + 1) % 2; for(int i = 0;i < 60 && state[i] < maximum;i++) { for(int j = 0;j < 60 && state[j] < maximum;j++) max = std::max(max,dp[td][i][j]); } printf("%d\n",max); } return 0; }