您的位置:首页 > 其它

HDU2167 Pebbles(状压DP)

2016-02-13 22:57 288 查看
题目给一张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=-1;
sscanf(s,"%d",&res);
while(*s==' ') ++s;
while(*s>='0' && *s<='9') ++s;
while(*s==' ') ++s;
return res;
}
int n,a[15][15],d[15][1<<15],sta[1<<15],sn;
bool isok(int s){
for(int i=1; i<n; ++i){
if(((s>>i-1)&1) && ((s>>i)&1)) return 0;
}
return 1;
}
bool isok(int x,int y){
for(int i=0; i<n; ++i){
if(((x>>i)&1)==0) continue;
if((y>>i)&1) return 0;
if(i>0 && ((y>>i-1)&1)) return 0;
if(i<n-1 && ((y>>i+1)&1)) return 0;
}
return 1;
}
int main(){
char str[1111];
while(gets(str) && *str){
n=0;
char *s=str; int t;
while(t=read(s),t!=-1) a[0][n++]=t;
for(int i=1; i<n; ++i){
gets(str); s=str;
for(int j=0; j<n; ++j) a[i][j]=read(s);
}
sn=0;
for(int i=0; i<(1<<n); ++i){
if(isok(i)) sta[sn++]=i;
}
memset(d,0,sizeof(d));
for(int i=0; i<sn; ++i){
for(int j=0; j<n; ++j){
if((sta[i]>>j)&1) d[0][sta[i]]+=a[0][j];
}
}
vector<int> vec[2222];
for(int i=0; i<sn; ++i){
for(int j=0; j<sn; ++j){
if(isok(sta[i],sta[j])) vec[i].push_back(j);
}
}
for(int i=1; i<n; ++i){
for(int j=0; j<sn; ++j){
for(int k=0; k!=vec[j].size(); ++k) d[i][sta[j]]=max(d[i][sta[j]],d[i-1][sta[vec[j][k]]]);
for(int k=0; k<n; ++k){
if((sta[j]>>k)&1) d[i][sta[j]]+=a[i][k];
}
}
}
int res=0;
for(int i=0; i<sn; ++i){
res=max(res,d[n-1][sta[i]]);
}
printf("%d\n",res);
getchar();
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: