LOJ#10173. 炮兵阵地【状压DP】

题目描述 https://loj.ac/problem/10173

1. 读入时转成两进制数,P为0,H为1(这样后面的时候方便判断状态是否合法)。m的范围为10,我们要同时枚举三行的状态,三层循环显然是超时的,所以我们需要预处理出可行的方案,其实才不到70种.同时用cal(i)记录状态i中有多少个1,即炮兵。

2.用 ans[i][j][k]表示第i行状态为k上一行状态为j时的方案数,枚举时注意判断三行中互不侵犯和本行是否可以这样放。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int ans[105][100][100],st[100],f[105],sum[100],cnt,an,tot;
int cal(int x)
{
	int cm=0;
	while(x!=0)
	{
		cm+=(x&1);
		x/=2;
	}
	return cm;
}
int main()
{ int n,m;

   scanf("%d%d",&n,&m);
   for(int i=1;i<=n;i++)
     for(int j=1;j<=m;j++)
     {
     	char a; cin>>a;
		if(a=='H') f[i]|=(1<<(j-1));//H为1 P为0    
	 }
   cnt=(1<<m)-1;//ans[i][j][k]表示第i行状态为k上一行状态为j
   for(int i=0;i<=cnt;i++)
     if(((i&(i<<1))==0)&&((i&(i<<2))==0))
       st[++tot]=i,sum[tot]=cal(i);//cal(i)为状态i中有多少个1 
   for(int i=1;i<=tot;i++)
     if((st[i]&f[1])==0) ans[1][1][i]=sum[i];
   for(int i=1;i<n;i++)//枚举行 
     for(int j=1;j<=tot;j++)//枚举上一行状态 
       for(int k=1;k<=tot;k++)//枚举本行状态 
         if((st[j]&st[k])==0)//合法 
           for(int p=1;p<=tot;p++)//枚举下一行状态 
             if(((st[j]&st[p])==0)&&((st[k]&st[p])==0)&&((st[p]&f[i+1])==0))
 	           ans[i+1][k][p]=max(ans[i+1][k][p],ans[i][j][k]+sum[p]);
   for(int i=1;i<=tot;i++)
     for(int j=1;j<=tot;j++)
	   an=max(an,ans[n][i][j]);
   printf("%d",an);   
 	 
  return 0;	
}

猜你喜欢

转载自blog.csdn.net/qq_42920122/article/details/87976539
今日推荐