HDU2167 Pebbles(状压DP)

时间:2023-03-08 19:08:46

题目给一张n×n的格子,每个格子都有数字,要从格子中取若干个数字,八个方向相邻的数字不能一起取,问取的数字最大和是多少。

从第一行一行一行看下去,可以发现第1行取哪几列只会影响到第2行,第3行后面的一点影响都没有。即第i行的决策只受i-1行决策的影响。

那么自然想到状态DP——

  • dp[i][S]前i行其中第i行取的列的集合是S的取数最大和
  • dp[i][S]=max(dp[i-1][S'])+集合S数字和(S是S'的合法的下一行的取法)

虽然题目n最多15,集合S就215种状态,但事实上合法的(不能同时取同一行相邻的两列数字)集合S状态只有1000多个。枚举即可转移,为了保证时间复杂度做一些预处理就行了。

 #include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
int read(char *&s){
int res=-;
sscanf(s,"%d",&res);
while(*s==' ') ++s;
while(*s>='' && *s<='') ++s;
while(*s==' ') ++s;
return res;
}
int n,a[][],d[][<<],sta[<<],sn;
bool isok(int s){
for(int i=; i<n; ++i){
if(((s>>i-)&) && ((s>>i)&)) return ;
}
return ;
}
bool isok(int x,int y){
for(int i=; i<n; ++i){
if(((x>>i)&)==) continue;
if((y>>i)&) return ;
if(i> && ((y>>i-)&)) return ;
if(i<n- && ((y>>i+)&)) return ;
}
return ;
}
int main(){
char str[];
while(gets(str) && *str){
n=;
char *s=str; int t;
while(t=read(s),t!=-) a[][n++]=t;
for(int i=; i<n; ++i){
gets(str); s=str;
for(int j=; j<n; ++j) a[i][j]=read(s);
}
sn=;
for(int i=; i<(<<n); ++i){
if(isok(i)) sta[sn++]=i;
}
memset(d,,sizeof(d));
for(int i=; i<sn; ++i){
for(int j=; j<n; ++j){
if((sta[i]>>j)&) d[][sta[i]]+=a[][j];
}
}
vector<int> vec[];
for(int i=; i<sn; ++i){
for(int j=; j<sn; ++j){
if(isok(sta[i],sta[j])) vec[i].push_back(j);
}
}
for(int i=; i<n; ++i){
for(int j=; j<sn; ++j){
for(int k=; k!=vec[j].size(); ++k) d[i][sta[j]]=max(d[i][sta[j]],d[i-][sta[vec[j][k]]]);
for(int k=; k<n; ++k){
if((sta[j]>>k)&) d[i][sta[j]]+=a[i][k];
}
}
}
int res=;
for(int i=; i<sn; ++i){
res=max(res,d[n-][sta[i]]);
}
printf("%d\n",res);
getchar();
}
return ;
}