luogu P2704【NOI2001】 & SSL1384 炮兵阵地【状压DP】

luogu P2704【NOI2001】 & SSL1384 炮兵阵地【状压DP】_第1张图片

状压DP

解题思路

首先对高山和平原做一个预处理
再枚举 i ( 1 , 2 < < n ) i(1,2<i1,2<<n,将二进制数存在 a [ i ] a[i] a[i] 里,将兵数存在 n u m [ i ] num[i] num[i] 里。
然后开始DP
f [ i ] [ l ] [ k ] f[i][l][k] f[i][l][k] 表示当第 i i i 行有 j j j 个炮兵, i i i 的上一行有 k k k 点贡献。
转移方程
f [ i ] [ l ] [ k ] = max ⁡ ( f [ i ] [ l ] [ k ] , f [ i − 1 ] [ k ] [ j ] + n u m [ l ] ) ; f[i][l][k]=\max(f[i][l][k],f[i-1][k][j]+num[l]); f[i][l][k]=max(f[i][l][k],f[i1][k][j]+num[l]);
这里我们不仅要判断是否可放炮兵,还要判断当前位置是不是山头。

代码

#include
#include
#include
#include
#include
using namespace std;

int map[1010],a[1<<10],num[1<<10];
int n,m,tot,ans;
char c;

int lowbit(int x)
{
	return x&(-x);
} 
bool check(int x)
{
	int cnt=0;
	while(x)
	 {
	 	if((x&1)&&cnt)
	 	  return 0;
	 	if(x&1)
	 	  cnt=3;
	 	if(cnt)
	 	  cnt--;
	 	x>>=1;
	 }
	return 1;
}
int count(int x)
{
	int c=0;
	for(int i=x; i>0; i-=lowbit(i))
	   c++;
	return c;
}
int main()
{
    cin>>n>>m;
    for(int i=1; i<=n; i++)
     for(int j=1; j<=m; j++)
      {
      	 cin>>c;
      	 if(c=='P')
      	   map[i]=(map[i]<<1);
      	 else
      	   map[i]=(map[i]<<1)+1;
      }
    for(int i=0; i<1<<m; i++)
     {
     	if(check(i))
     	 {
     	 	a[++tot]=i;
     	 	num[tot]=count(i);
     	 }
     }
    int f[110][tot+1][tot+1];
    for(int i=1; i<=n; i++)
     for(int j=1; j<=tot; j++)
      if(!(a[j]&map[i-2]))
       for(int k=1; k<=tot; k++)
        if(!(a[j]&a[k])&&!(a[k]&map[i-1]))
         for(int l=1; l<=tot; l++)
          if(!(a[j]&a[l])&&!(a[k]&a[l])&&!(a[l]&map[i]))
           f[i][l][k]=max(f[i][l][k],f[i-1][k][j]+num[l]);
    for(int i=1; i<=tot; i++)
     for(int j=1; j<=tot; j++)
        ans=max(ans,f[n][i][j]);
    cout<<ans;
    return 0;
}

你可能感兴趣的:(题解,dp,#,状压DP)