您的位置:首页 > 其它

Poj 1185 炮兵阵地 【状态压缩dp】

2010-06-11 09:25 281 查看
Poj 1185 炮兵阵地

NOI的经典题目。

首先看数据范围,列数C<=10,很容易诱导我们向状态dp方向考虑。
很多时候,题目的数据范围就决定了你的算法的复杂度,也影响着我们的思考方向。
把问题看做是多阶段决策过程。很容易发现,第i行选择的位置(状态),
只和i-1,i-2行情况有关,与i-2之前的那些行的决策无关。

用状态dp来做。用一个整数x表示该行的状态,
二进制中1表示该处放了炮兵,0表示没有。

这样,我们经过前两行的状态就能推到下面一行每个状态的最大个数。

令dp[i][j][k] 表示第i行状态为k,第i-1状态为j时的最大炮兵个数。
依次推至dp[i+1][k][t];

当然其中一些位运算技巧也很重要。比如求一个状态是否合法。
比如求一个数二进制中1的个数。
比如求两个状态是否可以共存。
因为使用较多。所以用一般方法实现的话,有可能TLE。
]// poj 1185 炮兵阵地   状态压缩dp
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;

int R,C,dp[110][70][70];
int stk[70],top;
int cur[110],num[110];
char s[110][20];
//判断状态x是否合法,即不存在相邻的1直接距离小于3的。
bool ok(int x)
{
if(x&(x<<1)) return 0;
if(x&(x<<2)) return 0;
return 1;
}
//找到所有可能的合法状态,最多60种
void jinit()
{
top=0;
int i,total=1<<C;
for(i=0;i<total;i++) if(ok(i)) stk[++top]=i;
}
//判断状态x是否与第k行匹配
bool fit(int x,int k)
{
if(cur[k]&x) return 0;
return 1;
}
//数一个整型数x的二进制中1的个数
int jcount(int x)
{
int cnt=0;
while(x)
{
cnt++;
x&=(x-1);
}
return cnt;
}
int main()
{
int i,j,k,t;
while(scanf("%d%d",&R,&C)==2)
{
jinit();
for(i=1;i<=R;i++)
{
scanf("%s",s[i]+1);
cur[i]=0;
for(j=1;j<=C;j++)
{
if(s[i][j]=='H') cur[i]+=(1<<(j-1));
}
}

memset(dp,-1,sizeof(dp));
//初始化第一行状态
for(i=1;i<=top;i++)
{
num[i]=jcount(stk[i]);
if(fit(stk[i],1)) dp[1][1][i]=num[i];
}

for(i=2;i<=R;i++)
{
for(t=1;t<=top;t++)
{
if(!fit(stk[t],i)) continue;
for(j=1;j<=top;j++)
{
if(stk[t]&stk[j]) continue;
for(k=1;k<=top;k++)
{
if(stk[t]&stk[k]) continue;
if(dp[i-1][j][k]==-1) continue;
dp[i][k][t]=max(dp[i][k][t],dp[i-1][j][k]+num[t]);
}
}
}
}

int ans=0;
for(i=1;i<=top;i++)
{
for(j=1;j<=top;j++) ans=max(ans,dp[R][i][j]);
}
printf("%d/n",ans);
}
system("pause");
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: