两个题目都是一样的套路..开始学习状压DP,记录一下 。
先预处理状态,找到可行的所有状态,然后遍历所有可能性。
先找单行所有可行状态,然后在进行运算的时候判断是否与上一行冲突,不冲突说明是一种方案,记录一下。
附上HDU2167代码:
#include<stdio.h>
#include<string.h>
#define M 1<<16
int a[20][20],dp[20][20000];
int s[20000];
void inti()//记录单行所有可取状态
{
int k=0,i;
for(i=0;i<M;i++)
{
if(i&(i<<1))
continue;
s[k++]=i;
}
}
int getsum(int i,int x)//某种状态所取得的和
{
int sum=0,t=0;
while(x)//末位是1就加上
{
if(x&1)
sum+=a[i][t];
t++;
x>>=1;
}
return sum;
}
int main()
{
char ch[10005];
int r=0,c=0;
int i,j,k;
inti();
while(gets(ch))
{
if(strcmp(ch,"")!=0)
{
int num=0;
for(i=0;ch[i]!='\0';i++)
{
if(ch[i]==' ')
{
a[r][c++]=num;
num=0;
}
else
num=num*10+ch[i]-'0';
}
a[r][c]=num;
r++;c=0;
}
else
{
int x=1<<r;
for(i=0;s[i]<x;i++)
dp[0][i]=getsum(0,s[i]);
for(i=1;i<r;i++)
{
for(j=0;s[j]<x;j++)
{
int max=0;
dp[i][j]=getsum(i,s[j]);
for(k=0;s[k]<x;k++)
{
if(s[j]&s[k]||s[j]&s[k]<<1||s[j]<<1&s[k])//相邻对角线也不能取...
continue;
if(dp[i-1][k]>max)
max=dp[i-1][k];
}
dp[i][j]+=max;
}
}
int ans=0;
for(i=0;s[i]<x;i++)
{
if(dp[r-1][i]>ans)
ans=dp[r-1][i];
}
printf("%d\n",ans);
memset(a,0,sizeof(a));
memset(dp,0,sizeof(dp));
r=0;c=0;
}
}
return 0;
}