hdoj 1565 dp
http://acm.hdu.edu.cn/showproblem.php?pid=1565
方格取数(1)
Time Limit: 1000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1162 Accepted Submission(s): 415
Problem Description
给你一个n*n的格子的棋盘,每个格子里面有一个非负数。
从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取的数所在的2个格子不能相邻,并且取出的数的和最大。
从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取的数所在的2个格子不能相邻,并且取出的数的和最大。
Input
包括多个测试实例,每个测试实例包括一个整数n 和n*n个非负数(n<=20)
Output
对于每个测试实例,输出可能取得的最大的和
Sample Input
3 75 15 21 75 15 28 34 70 5
Sample Output
188
这个问题可以用网络流来解决 最大点权独立集
这里由于数据小 我用动态规划解决
设二进制1为取该数 0为不取
设dp[i][j]为取完前i行数中 第i行为二进制j状态的最大值
比如dp[3][5]=20 表示取完前3行 第三行取了第一个和最后一个数(5的二进制为101) 的最大值
则状态转移方程
dp[i][j]=max(dp[i-1][k]+curline); 其中(k & j ==0)
其中状态k,j不是所有数都可以取 状态二进制表示中不能有连续的1 可以先预处理筛选出来
注意:此题测试数据中有n==0 输出0
#include<stdio.h>
#include<string.h>
#define MAXN 200000
int dp[2][MAXN]; //滚动数组
int stu[MAXN]; //表示所有合法状态 二进制中没有连续的1
int bit[22]; //bit[i] 表示只取从右到左第i个数的状态 如000100 只取第3个
int v[22]; //保存每行读入的数
int main()
{
int i,j,k,n,curi,prei,ans;
int num=0,top=1<<20;
bit[1]=1;
for(i=2;i<=21;i++)
bit[i]=bit[i-1]<<1;
//下面预处理出所有合法状态
for(i=0;i<top;i++)
{
for(j=1;bit[j]<=i;j++)
{
if((i & bit[j]) && (i & bit[j-1]))
break;
}
if(bit[j]>i)
stu[num++]=i;
}
//printf("%d\n%d\n",num,stu[num-1]);
while(~scanf("%d",&n))
{
memset(dp,0,sizeof(dp));
ans=0;
top=1<<n;
for(i=0;i<n;i++)
{
curi=i&1;
prei=curi^1;
for(j=0;j<n;j++)
scanf("%d",v+j);
for(j=1;stu[j]<top;j++)
{
int s=0,curmax=0;
for(k=1;bit[k]<=stu[j];k++)
{
if(bit[k] & stu[j])
s+=v[n-k];
}
for(k=0;stu[k]<top;k++)
{
if(!(stu[k] & stu[j]))
curmax=curmax>dp[prei][k]?curmax:dp[prei][k];
}
dp[curi][j]=curmax+s;
if(ans<dp[curi][j])
ans=dp[curi][j];
//printf("dp[%d][%d]=%d\n",i,stu[j],dp[i&1][j]);
}
}
printf("%d\n",ans);
}
return 0;
}