POJ1185 [NOI2001] 炮兵阵地 (状压DP)
又是一道有合法性检测的状压题。
dp[i][j][k]表示第i行状态为j,i-1行状态为k时前i行放置的最大数量。
注意22行统计二进制数中1的个数时的巧妙方法。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 int N,M; 6 char map[110][20],num[70],top; 7 int stk[70],cur[70]; 8 int dp[110][70][70]; 9 10 bool check(int x){//判断该状态是否合法,横向检测相邻的1之间的距离不能小于3 11 if(x&(x<<1)) return 0; 12 if(x&(x<<2)) return 0; 13 return 1; 14 } 15 16 void init(){//记录所有可能的合法状态,最多60种 17 top=0; 18 for(int i=0;i<(1<<M);i++) 19 if(check(i)) stk[top++]=i; 20 } 21 22 int count(int x){//统计状态x的二进制中1的个数 23 int cnt=0; 24 while(x){ 25 cnt++; 26 x&=(x-1);//很巧妙的求解方法 27 } 28 return cnt; 29 } 30 31 bool fit(int x,int k){ 32 if(cur[k]&x) return 0; 33 return 1; 34 } 35 36 int solve(){ 37 int ans=0; 38 memset(dp,-1,sizeof(dp)); 39 for(int j=0;j<top;j++){//初始化第一行状态 40 num[j]=count(stk[j]);//统计每个合法状态中1的个数 41 if(fit(stk[j],1)){ 42 dp[1][j][0]=num[j];//第一行状态为j,上一行状态为第0个状态,即000000 43 ans=max(ans,dp[1][j][0]); 44 } 45 } 46 for(int i=2;i<=N;i++) 47 for(int j=0;j<top;j++){ 48 if(!fit(stk[j],i)) continue; 49 for(int k=0;k<top;k++){ 50 if(stk[j]&stk[k]) continue; 51 for(int t=0;t<top;t++){ 52 if(stk[j]&stk[t]) continue; 53 if(dp[i-1][k][t]==-1) continue; 54 dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][t]+num[j]); 55 } 56 if(i==N) ans=max(ans,dp[i][j][k]); 57 } 58 } 59 return ans; 60 } 61 62 int main(){ 63 while(~scanf("%d%d",&N,&M)){ 64 init(); 65 for(int i=1;i<=N;i++) 66 scanf("%s",map[i]+1); 67 for(int i=1;i<=N;i++){ 68 cur[i]=0; 69 for(int j=1;j<=M;j++) 70 if(map[i][j]=='H') 71 cur[i]+=(1<<(j-1)); 72 } 73 printf("%d\n",solve()); 74 } 75 return 0; 76 }