poj 1185 炮兵阵地
dede说这是一道很经典的题。
Time Limit: 2000MS | Memory Limit: 65536K | |
Total Submissions: 14887 | Accepted: 5585 |
Description
![](http://poj.org/images/1185_1.jpg)
Input
Output
Sample Input
5 4
PHPP
PPHH
PPPP
PHPP
PHHP
Sample Output
6
————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
刚才poj上贴了代码过了,自己还没实现过。
据说是状态压缩?(啥意思= =)
首先要知道的:我认为“压缩”的关键就是用二进制来表示和用位运算判断一种情况是否合法:
用1来表示当前位置是山(H)或在炮兵的攻击范围内,用0表示当前位置可以布置炮兵(是平原并且不在别的炮兵攻击范围里)然后将这一行看做一个二进制数。(下文中的m,n,k1,k2,k'就是十进制形式)
总体思路:1.预处理出地图的状态保存的数组h[i]中。
2.由于x[i][j]是否能放炮兵是由它上下左右2格范围所决定的,故①左右只要满足二进制表示的数1所在的位置两格内没有别的1。②[i-1][j],[i-2][j]不为1。就是合法的。
3.状态转移方程dp[i+1][k'][k1]=dp[i][k1][k2]+n(k')
dp[i][m][n]表示在i-1行是状态m,i-2行是状态n下前i行所放的炮兵数。n(k')表示k'转化为二进制后有多少个1。
具体实现:1.建立一个函数用于枚举行的状态。判断同行的炮兵之间是否相互攻击((x<<1)& x)|(x<<2)& x),把每行的合法状态保存到一个数组cnt[]里。
2.开始循环枚举。
{
枚举每一行i
{
枚举i行的可能性
{
枚举i-1行的可能性
{
判断i 与i-1行是否冲突
}
枚举i-2行的可能性
{
判断i 与i-1行是否冲突
}
}
3.找dp[n][k1][k2]中最大的即为答案。k1,k2∈cnt[]。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<stdio.h> 2 #include<string.h> 3 int cnt,m,n; 4 int dp[100][64][64],num[64],state[64],bitmap[100]; 5 void init() 6 { 7 int tmp; 8 cnt=0; 9 for(int i=0;i<(1<<m);i++) //枚举每行的状态,从0到2^m - 1,判断其是否合法 10 { 11 tmp=i; 12 if( ((tmp<<1)&i) | ((tmp<<2)&i) ) continue;//判断该行在这个状态时是否合法(任意炮兵都不在其他炮兵的攻击范围之内) 13 state[cnt]=i; //通过数组state[]记录合法的状态(十进制表示) 14 num[cnt]=tmp&1; //num[]数组记录这个合法状态下‘1’的个数(也就时炮兵的个数) 15 while( tmp = (tmp>>1) ) 16 num[cnt]+=tmp&1; 17 cnt++; //此函数的统计是假设当该行都为平地时,即共有cnt+1个合法状态 18 } 19 } 20 void solve() 21 { 22 int ans,i,j,k,p; 23 memset(dp,0,sizeof(dp)); 24 for(i=0;i<n;i++) //枚举每一行 25 for(j=0;j<cnt;j++) //先枚举第i行的可能状态 26 { 27 if(bitmap[i]&state[j]) continue; //地图中标记为“山地”的点不能布兵 28 if(i==0) dp[i][j][0]=num[j]; 29 else if(i==1) 30 { 31 for(k=0;k<cnt;k++) //枚举第i-1行的可能状态 32 { 33 if(bitmap[i-1]&state[k]) continue; //判断第i-1行的k状态是否和山地冲突,冲突就跳到下一个状态k+1 34 if(state[j]&state[k]) continue; //判断上下两行(i-1和i)的合法状态是否兼容彼此 35 if(dp[i][j][k]<dp[i-1][k][0]+num[j]) 36 dp[i][j][k]=dp[i-1][k][0]+num[j]; 37 } 38 } 39 else 40 { 41 for(k=0;k<cnt;k++) //枚举第i-1行的可能状态 42 { 43 if(bitmap[i-1]&state[k]) continue; //判断第i-1行的k状态是否和山地冲突,冲突就跳到下一个状态k+1 44 if(state[j]&state[k]) continue; //判断上下两行(i-1和i)的合法状态是否兼容彼此 45 for(p=0;p<cnt;p++) //枚举第i-2行的可能状态 46 { 47 if(bitmap[i-2]&state[p]) continue; //判断第i-2行的p状态是否和山地冲突,冲突就跳到下一个状态p+1 48 if(state[k]&state[p] || state[j]&state[p]) continue;//判断上下两行(i-2和i-1,i-2和i)的合法状态是否兼容彼此 49 if(dp[i][j][k]<dp[i-1][k][p]+num[j]) 50 dp[i][j][k]=dp[i-1][k][p]+num[j]; 51 } 52 } 53 } 54 } 55 56 ans=0; 57 //for(i=0;i<n;i++) 58 for(j=0;j<cnt;j++) 59 for(k=0;k<cnt;k++) 60 if(dp[n-1][j][k]>ans) 61 ans=dp[n-1][j][k]; 62 printf("%d\n",ans); 63 } 64 int main() 65 { 66 char s[12]; 67 int i,j; 68 while(scanf("%d%d",&n,&m)!=EOF) 69 { 70 memset(bitmap,0,sizeof(bitmap)); 71 for(i=0;i<n;i++) 72 { 73 scanf("%s",s); 74 for(j=0;j<m;j++)if(s[j]=='H')bitmap[i]+=(1<<(m-1-j)); 75 //if(s[j]=='H')bitmap[i]|=(1<<j); 76 } 77 init(); 78 solve(); 79 } 80 return 0; 81 82 }