[NOI2001]炮兵阵地 状压DP
题面:
司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队。一个N*M的地图由N行M列组成,地图的每一格可能是山地(用“H” 表示),也可能是平原(用“P”表示),如下图。在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示:
如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。 现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。
n <= 100,m <= 10;
题解:
观察到如此小的m,我们首先就要考虑状压,
但是每一行将会受到2行的影响,因此我们考虑压两行,
f[i][j][k]表示第i行状态为j,第i-1行状态为k的最优解,
因此我们有转移方程:
f[i][j][k]=max(f[i][j][k],f[i-1][k][l] + num[j].one);
num[j].one 表示状态j下有几个炮兵,
合法条件如下:
if(s[i] & num[j].date) continue;
if(s[i-1] & num[k].date) continue;
if(num[j].date & num[k].date) continue;
if(s[i-2] & num[l].date) continue;
if((num[j].date & num[l].date) | (num[k].date & num[l].date)) continue;
其中s[i]表示i行的限制条件,如果第t位不能放炮兵,那么这位就是1,
j , j ,l 分别为i,i-1,i-2所枚举的状态,
num[i].date表示当前状态,
因为要满足不能放炮兵的地方不放炮兵,所以s[i] & num[j].date必须为0,其他行同理,
这时你肯定注意到了,,,这是个4层循环啊!那复杂度岂不是100 * 1024 *1024 *1024?
这样的复杂度当然是不行的,观察到每个炮兵的管辖区域相对于仅仅只有10的m来说,其实是非常广的,不管怎么放,最多也就放4个炮兵而已,
因此我们可以实现找出所有初步合法状态(即满足同一行中炮兵不互相贡献的状态),一共只有59个,,,,
因此这时的复杂度就可以承受了,
而且找合法状态的时候还可以顺便就预处理出对应的炮兵个数,是不是非常方便啊~~~~~~~
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define R register int 4 #define AC 110 5 int n,m,tot,ans; 6 int f[AC][65][65]; 7 int s[AC]; 8 char ss[AC]; 9 struct node{ 10 int date,one; 11 }num[AC]; 12 /*观察到因为m只有10,而一个炮兵的控制区域就有5了,所以一行最多放两个 13 可行状态很少,m=10都只有59个,所以完全可以打表放进来啊! 14 f[i][j][k]表示i行状态j,上一行为k*/ 15 inline bool cmp(node a,node b) 16 { 17 return a.date < b.date; 18 } 19 20 void pre() 21 { 22 scanf("%d%d",&n,&m); 23 for(R i=1;i<=n;i++) 24 { 25 scanf("%s",ss+1); 26 for(R j=1;j<=m;j++) 27 if(ss[j] == 'H') s[i] |= (1 << (m - j));//获取这个的状态,1表示不合法 28 } 29 } 30 31 void work1() 32 { 33 ++tot; 34 for(R i=1;i<=m;i++) 35 { 36 num[++tot].date=(1 << (i - 1)); 37 num[tot].one=1; 38 } 39 for(R i=1;i<=m;i++) 40 for(R j=i+3;j<=m;j++) 41 { 42 num[++tot].date=(1 << (i - 1)) + (1 << (j - 1)); 43 num[tot].one=2; 44 } 45 for(R i=1;i<=m;i++) 46 for(R j=i+3;j<=m;j++) 47 for(R k=j+3;k<=m;k++) 48 { 49 num[++tot].date=(1 << (i - 1)) + (1 << (j - 1)) + (1 << (k - 1)); 50 num[tot].one=3; 51 } 52 for(R i=1;i<=m;i++) 53 for(R j=i+3;j<=m;j++) 54 for(R k=j+3;k<=m;k++) 55 for(R l=k+3;l<=m;l++) 56 { 57 num[++tot].date=(1 << (i - 1)) + (1 << (j - 1)) + (1 << (k - 1)) + (1 << (l - 1)); 58 num[tot].one=4; 59 } 60 // for(R i=1;i<=tot;i++) 61 // printf("%d have %d\n",num[i].date,num[i].one); 62 } 63 64 void work() 65 { 66 memset(f,128,sizeof(f)); 67 for(R i=1;i<=tot;i++) 68 { 69 if(s[1] & num[i].date) continue; 70 f[1][i][0]=num[i].one;//获取第一行 71 } 72 for(R i=1;i<=tot;i++) //枚举第二行的状态 73 { 74 if(s[2] & num[i].date) continue; 75 for(R j=1;j<=tot;j++) 76 { 77 if(s[1] & num[j].date) continue; 78 if(num[i].date & num[j].date) continue; 79 f[2][i][j]=f[1][j][0] + num[i].one; 80 } 81 } 82 for(R i=3;i<=n;i++)//枚举到了哪一行 83 { 84 for(R j=1;j<=tot;j++)//枚举当前行状态 85 { 86 if(s[i] & num[j].date) continue;//山地不能放 87 for(R k=1;k<=tot;k++)//枚举上一行状态 88 { 89 if(s[i-1] & num[k].date) continue; 90 if(num[j].date & num[k].date) continue;//不能在同一个位置有炮 91 for(R l=1;l<=tot;l++)//枚举上上行状态 92 { 93 if(s[i-2] & num[l].date) continue; 94 if((num[j].date & num[l].date) | (num[k].date & num[l].date)) continue;//都不能相互冲突 95 f[i][j][k]=max(f[i][j][k],f[i-1][k][l] + num[j].one); 96 } 97 } 98 } 99 } 100 for(R i=1;i<=tot;i++)//枚举最后一行的状态 101 for(R j=1;j<=tot;j++) 102 { 103 if((s[n] & num[i].date) || (s[n-1] & num[j].date)) continue; 104 ans=max(ans,f[n][i][j]);//是否冲突,,,就懒得判了吧,反正也是0 105 } 106 printf("%d\n",ans); 107 } 108 109 int main() 110 { 111 // freopen("in.in","r",stdin); 112 pre(); 113 work1();//先找到所有有效情况 114 work(); 115 // fclose(stdin); 116 return 0; 117 }