POJ_1185_炮兵阵地 dp+状态压缩

题目:炮兵阵地

链接:http://poj.org/problem?id=1185

解题思路:

  首先用 int 来表示每一行的情况,比如说第一行是k1,那么【 k1&(k1>>2) | k1&(k1>>1) 】就可以排除一行中相邻的和隔一格的。。。

  地形也可以用 int (取反)来表示,比如说第一行是PPHPH,给他记作m1=00101(二进制数),1表示不能存放,那么对于一个数a,判断a是不是符合地形就可以用【 a & m1 】来表示,如果与出来的是真,那么说明存在某一位1&1的情景,就表示不符合。

  下一行是否能和上一行共存也用位运算来进行,比如下一行是s2,上一行是s1,那么【 s2 & s1 】为真就表示不能共存。

  上面是通过位运算来巧妙解决可行存放问题的方法。

  贴代码了:

  

  1 #include<stdio.h>
  2 #include<string.h>
  3 /*
  4   改进。。。
  5 */
  6 char s[100][11];
  7 int mp[100];
  8 int ans[60],ao;
  9 int dp[100][60][60];
 10 /*
 11   dp[i][j][k]: 第i行 - 上一行是ans[j]、本行是ans[k]的情况下,最多数量为多少。
 12   因为有了两行的间隔,上面的炮台就不会影响到下面的,所以只要记录两行的所有情况,两行相同的可以计算出最大值了
 13 */
 14 //因为列数最多为10,所以无视地形,一行最多有60中存放方式
 15 void func(int m)
 16 {
 17   for(int i=0;i<(1<<m);i++)
 18   {
 19     if(i&(i>>1)||i&(i>>2)) continue;
 20     ans[ao++]=i;
 21   }
 22 }
 23 int count(int x)
 24 {
 25   int co=0;
 26   while(x)
 27   {
 28     co+=x&1;
 29     x>>=1;
 30   }
 31   return co;
 32 }
 33 int pre[100][10000],po,ko,lo;
 34 int main()
 35 {
 36   int n,m;
 37   while(scanf("%d%d",&n,&m)!=EOF)
 38   {
 39     for(int i=0;i<n;i++)
 40     {
 41       scanf("%s",s[i]);
 42     }
 43     memset(mp,0,sizeof(mp));
 44     for(int i=0;i<n;i++)
 45     {
 46       for(int j=0;j<m;j++)
 47       {
 48         if(s[i][j]=='H') mp[i]=mp[i]|(1<<j);
 49       }
 50     }
 51     ao=0;
 52     func(m);
 53     memset(dp,0,sizeof(dp));
 54     po=0;
 55     for(int i=0;i<ao;i++)
 56     {
 57       if(mp[0]&ans[i]) continue;
 58       int tmp=count(ans[i]);
 59       dp[0][0][i]=tmp;
 60       pre[0][po++]=i;   //记录第0行所有存值情况,po为总数
 61     }
 62     if(n==1)
 63     {
 64       int mt=0;
 65       for(int i=0;i<po;i++)
 66       {
 67         int line1=pre[0][i];  //因为前面只有一行,所以只需要考虑pre[0][i]的情况
 68         if(dp[0][0][line1]>mt) mt=dp[0][0][line1];
 69       }
 70       printf("%d
",mt);
 71       continue;
 72     }
 73     ko=0;
 74     for(int i=0;i<po;i++)
 75     {
 76       for(int j=0;j<ao;j++)
 77       {
 78         int line1=pre[0][i];
 79         if(ans[j]&mp[1]) continue;
 80         if(ans[line1]&ans[j]) continue;
 81         int tmp=count(ans[j]);
 82         dp[1][line1][j]=dp[0][0][line1]+tmp;
 83         pre[2][ko]=line1;  //到下一行时,这里就是上两行,下面的就是上一行,先用2暂存,最终再移入0
 84         pre[1][ko++]=j;
 85       }
 86     }
 87     for(int i=0;i<ko;i++)
 88       pre[0][i]=pre[2][i];
 89     bool v[70][70];
 90     for(int i=2;i<n;i++)
 91     {
 92       lo=0;
 93       memset(v,0,sizeof(v));
 94       for(int k=0;k<ko;k++)
 95       {
 96         int line1=pre[i-2][k],line2=pre[i-1][k];   //上两行和上一行的情况
 97         if(v[line1][line2]) continue;     //因为有很多重复的,去重
 98         v[line1][line2]=1;
 99         for(int u=0;u<ao;u++)
100         {
101           if(ans[u]&mp[i]) continue;  //如果与本身的地形不符,就跳过
102           if(ans[u]&ans[line1]||ans[u]&ans[line2]) continue;  //如果和前两行矛盾就跳过
103           int tmp=count(ans[u]);      //如果都满足条件,就计算这一行这种放法增加的数量
104           if(dp[i-1][line1][line2]+tmp>dp[i][line2][u])
105           {
106             dp[i][line2][u]=dp[i-1][line1][line2]+tmp;
107             pre[i+1][lo]=line2;       //先用i+1的存放,等k的循环结束再移到i-1
108             pre[i][lo++]=u;
109           }
110         }
111       }
112       for(int u=0;u<lo;u++)
113         pre[i-1][u]=pre[i+1][u];
114       ko=lo;
115     }
116     int mt=0;
117     for(int j=0;j<ko;j++)
118     {
119       int line1=pre[n-2][j],line2=pre[n-1][j];
120       if(mt<dp[n-1][line1][line2]) mt=dp[n-1][line1][line2];
121     }
122     printf("%d
",mt);
123   }
124   return 0;
125 }
原文地址:https://www.cnblogs.com/hchlqlz-oj-mrj/p/5058059.html