状压dp入门题 昨天调好久了结果zz把判断行内相交的函数写错没调出来 位运算优先级搞错了)
#include<iostream>
#include<cstdio>
#include<cctype>
#include<cmath>
//状态:dp[i][j][k]表示当前在第i行 i行状态编号为j i-1行状态编号为k的最大格子数
//答案即为dp[n][i][j] 中枚举i和j找到的最大值
using namespace std;
const int maxn=110;
const int maxm=10;
int map[maxn]; //每一行的合法状态并集
int dp[maxn][70][70];//需要保存当前行与上一行的状态
int state[100]; //60种状态集合
int sum[100]; //60种状态对应的可摆放机器人个数
bool fit(int x,int y){return (x|y)==y;} //集合x能放入y
bool ok(int x){ return !(x&(x>>1)||x&(x>>2));} //行内不相交
bool not_i(int x,int y){return !(x&y);}
int count(int x){
int s=0;
while(x){
s+=(x&1);
x>>=1;
}
return s;
}
int main(){
int n,m;
cin>>n>>m;
//可选格子(P)记为1 不可选记为0
for(int i=1;i<=n;++i)
for(int j=0;j<m;++j)
{
char c=getchar();
while(!isalpha(c)) c=getchar();
if(c=='P') map[i]|=(1<<j); //存储每行的可用格子
}
//预处理出60种状态
int cnt=0;
for(int i=0;i<(1<<m);i++){
if(ok(i)){ //合法的行内状态
state[++cnt]=i;
if(cnt>60)
{printf("%d\n",cnt);return 0;}
sum[cnt]=count(i);
//out<<sum[cnt]<<endl;
}
}
//对第一行和第二行需要自行判断
for(int i=1;i<=cnt;i++)
{
if(fit(state[i],map[1]))
dp[1][i][0]=sum[i];
//cout<<sum[i]<<endl;
}
for(int i=1;i<=cnt;i++){
if(!fit(state[i],map[2])) continue; //找出一种含于map[2]的状态
for(int j=1;j<=cnt;j++)
{
if(!fit(state[j],map[1])) continue;//找第一行的合法状态
if(not_i(state[i],state[j])) //判断第一行与第二行是否相交
{
dp[2][i][j]=sum[i]+sum[j];
//cout<<dp[2][i][j]<<endl;
}
}
}
int res=0;
for(int i=3;i<=n;i++)
{
for(int j=1;j<=cnt;j++){
if(!fit(state[j],map[i])) continue;//找到第i行的某一状态
for(int k=1;k<=cnt;k++)//上一行的状态
{
if(!fit(state[k],map[i-1])||!not_i(state[j],state[k])) continue;//确保state[i]能放进map[i-1]
for(int l=1;l<=cnt;l++){
if(!fit(state[l],map[i-2])||!not_i(state[k],state[l])||!not_i(state[j],state[l])) continue;
dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][l]+sum[j]);
// cout<<dp[i][j][k]<<endl;
}
}
}
}
for(int i=1;i<=cnt;i++){
for(int j=1;j<=cnt;j++)
{
res=max(res,dp[n][i][j]);
}
}
cout<<res<<endl;
return 0;
}