HDU-1565-方格取数(1)

HDU-1565-方格取数(1)

http://acm.hdu.edu.cn/showproblem.php?pid=1565

我的第一个状态压缩DP

给你一个n*n的格子的棋盘,每个格子里面有一个非负数,从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取的数所在的2个格子不能相邻,并且取出的数的和最大

3

75 15 21

75 15 28

34 70 5

188

对于每一个数字,或取或不取,记1为取该数,0为不取该数,对于每行的数来说,它的状态就可以用一个二进制的数来描述,对于第一行,若果我们取75,21,我们就可以用二进制的5来描述,即101,因为取的数所在的2个格子不能相邻,所以每一行的二进制数不能有相邻的1,再来看列,相邻的两行不能有相邻的,对于两个二进制,也就是两个数相与(&)为0,这样就可以得到当前的行和上一行的关系,dp[i][j]=dp[i-1][k]+sum(j)

dp[i][j]表示第i行在j状态,dp[i-1][k] 表示第i-1行在k状态,sum(j)表示第i行在状态k下所取数的和,当然k&j==0

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
using namespace std;
int num[21];
int k,st[18800]; //共有k中状态
int dp[2][18800];
int map[25][25];
int n;
int max(int x,int y)
{
    return x>y?x:y;
}
void init1()   //初始化num,n位数2进制数最多有num[n]种
{
    int i;
    num[0]=1;
    for(i=1;i<=20;i++)
    num[i]=num[i-1]<<1;
}
int judge(int x)
{
    int end=0;
    while(x)
    {
        if(end==1&&x%2)
        return 0;
        end=x%2;
        x/=2;
    }
    return 1;
}
void init2()
{
    int i;
    init1();
    k=0;
    for(i=0;i<=num[20];i++)
    if(judge(i))
    st[k++]=i;
    //printf("%d\n",k);  //k=17712,这个要事先求出才能知道数组该开多大
}
int main()
{
    int i,j,h;
    int Max,temp;
    init2();
    while(scanf("%d",&n)!=EOF)
    {
        if(n==0)
        {
            printf("0\n");
            continue;
        }
        for(i=0;i<n;i++)
        for(j=0;j<n;j++)
        scanf("%d",&map[i][j]);
        Max=-1;
        memset(dp,0,sizeof(dp));
        for(i=0;i<k;i++)  //先算第一行
        {
            if(st[i]>=num[n])  //n位数2进制数最多有num[n]种
            break;
            for(j=0;j<n;j++)  //把每种状态的数从右至左相加
            if(st[i]&num[j])
            dp[0][i]+=map[0][j];
            if(dp[0][i]>Max)
            Max=dp[0][i];
        }
        for(i=1;i<n;i++)
        {
            for(j=0;j<k;j++)
            {
                if(st[j]>=num[n])
                break;
                temp=0;
                for(h=0;h<n;h++)
                if(st[j]&num[h])
                temp+=map[i][h];
                for(h=0;h<k;h++)
                {
                    if(st[h]>=num[n])
                    break;
                    if(!(st[h]&st[j]))
                    dp[1][j]=max(dp[1][j],dp[0][h]+temp);
                    Max=max(Max,dp[1][j]);
                }
            }
            for(j=0;j<k;j++)
            {
                if(st[j]>num[n])
                break;
                dp[0][j]=dp[1][j];
                dp[1][j]=0;
            }
        }
        printf("%d\n",Max);
    }
    system("pause");
    return 0;
}


你可能感兴趣的:(HDU-1565-方格取数(1))