狀壓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;
}