题意
在
n
×
n
n\times n
n×n的棋盘放若干个炮兵,有的位置不能放,每一个炮兵的攻击范围如下:
你需要放置最多的炮兵。问最多能放多少。
解题方法
首先对𝑚状压。
设
d
p
[
i
]
[
s
1
]
[
s
2
]
dp[i][s1][s2]
dp[i][s1][s2]表示到了第i行,这一行放的状态是s1,上一行放的状态是s2的方案数。
然后枚举下一行放的情况,判断是否合法进行转移。
复杂度是
O
(
n
×
8
m
)
O(n\times 8^m )
O(n×8m),显然过不了。
但是注意到每一行的合法状态非常有限,因为相邻两个位置至少差3。
经过爆搜发现其实合法的状态只有70不到……
所以复杂度变成了
O
(
7
0
3
×
n
)
O(70^3\times n)
O(703×n),可以通过本题。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
using namespace std;
int n,m;
int zt[1024],s[1024];
int tot;
int dp[3][1024][1024];
int a[110];
void dfs(int he,int sum,int w)
{
if(w>=m)
{
tot++;
zt[tot]=he;
s[tot]=sum;
return;
}
dfs(he,sum,w+1);
dfs(he+(1<<w),sum+1,w+3);
}
int main()
{
scanf("%d%d",&n,&m);
dfs(0,0,0);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
char c;
c=getchar();
while(c!='P'&&c!='H')c=getchar();
a[i]<<=1;
if(c=='H')a[i]+=1;
}
}
for(int i=1;i<=tot;i++)
if((zt[i]&a[1])==0)
dp[1][0][i]=s[i];
for(int i=1;i<=tot;i++)
if((zt[i]&a[1])==0)
for(int j=1;j<=tot;j++)
if((zt[j]&a[2])==0)
if((zt[i]&zt[j])==0)
dp[2][i][j]=max(dp[2][i][j],dp[1][0][i]+s[j]);
for(int i=3;i<=n;i++)
{
for(int j=1;j<=tot;j++)
if((zt[j]&a[i])==0)
for(int k=1;k<=tot;k++)
if((zt[j]&zt[k])==0)
if((zt[k]&a[i-1])==0)
{
dp[i%3][k][j]=0;
for(int t=1;t<=tot;t++)
if((zt[t]&zt[j])==0)
if((zt[t]&zt[k])==0)
if((zt[t]&a[i-2])==0)
dp[i%3][k][j]=max(dp[i%3][k][j],dp[(i-1)%3][t][k]+s[j]);
}
}
int ans=0;
for(int i=1;i<=tot;i++)
for(int j=1;j<=tot;j++)
if((zt[i]&zt[j])==0)
if((zt[i]&a[n-1])==0)
if((zt[j]&a[n])==0)
ans=max(ans,dp[n%3][i][j]);
cout<<ans<<'\n';
return 0;
}