HDU 1565 方格取数(1)<<状压dp

题意

给定n*n的一个矩阵,每个格子有个非负数,要求是取了这个数就不能取它相邻的数,要使得取得数总和最大。

思路

这题一开始完全不知道怎么搞,查了题解之后发现有状压的做法和网络流(最大团)的做法,然而最大团已经触及我的知识盲区了,有空再补一下这个做法。

这里谈状压dp的解法。限于空间和时间,当然不能把整个棋盘都压缩成一个状态,仔细观察后发现,一行能怎么取数只和当前行以及相邻行相关,于是,我们只要按行枚举,把一行的状态压缩,判断与相邻行的关系来确定是否可行,然后状态转移。在此预处理出一行的可能状态数,发现n=20时可能的状态数仅有20000种不到。

由此,设dp[i][j]代表第i行状态为j时的最大取值,状态转移方程为$dp[i][k]=max(dp[i][k],dp[i-1][j])$其中j与k两状态相容,怎么判断相容呢?显然只要保证j与k每一位都不同即可。

最简单的表示法是$(state[j]&state[k])==0$此处要注意优先级,另一种写法是$(state[j]|state[k])==state[j]+state[k]$

这题卡了空间,按这样开开不下一行所有的状态数,此处有两种解决方案,最好的解决方案我认为就是离散化状态,只保留可行状态(20000种),另一种就是滚动数组啦,因为状态转移时只要考虑当前行和上一行。当然也可以两种都用,将空间节省到极致,但是你两个数组在倒空间的时候会浪费时间,所以我觉得此处没有必要这么写,这样写还增加了码量……。

还有一个细节,就是根据枚举的状态算当前行的值的时候,如果在线算,就相当于多了一个n的复杂度,所以大概会慢十倍左右,我第一份代码就是在线算的。仔细一想,这部分可以完全事先处理好。

代码

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int arr[25][25];
 4 vector<int> all;
 5 int n;
 6 int dp[22][1<<15];
 7 bool check(int state)
 8 {
 9     while(state)
10     {
11         if(state&1&&state&2) return false;
12         state>>=1;
13     }
14     return true;
15 }
16 void db()
17 {
18     all.clear();
19     for(int i=0;i<(1<<n);i++)
20     {
21         if(check(i))
22             all.push_back(i);
23     }
24 }
25 int cal(int r,int state)
26 {
27     int ret=0;
28     for(int i=n-1;i>=0;i--)
29     {
30         if(state&1)
31             ret+=arr[r][i];
32         state>>=1;
33     }
34     return ret;
35 }
36 int main()
37 {
38     while(~scanf("%d",&n))
39     {
40         db();
41         for(int i=0;i<n;i++)
42             for(int j=0;j<n;j++)
43                 scanf("%d",&arr[i][j]);
44         memset(dp,0,sizeof(dp));
45         int ans=0;
46         for(int i=0;i<n;i++)
47         {
48             for(int j=0;j<all.size();j++)
49             {
50                 if(i==0)
51                 {
52                     dp[i][j]=cal(i,all[j]);
53                     ans=max(ans,dp[i][j]);
54                     //cout<<bitset<3>(all[j])<<","<<dp[i][all[j]]<<endl;
55                 }
56                 else{
57                     for(int k=0;k<all.size();k++)
58                     {
59                         if(!(all[j]&all[k]))
60                         {
61                             //cout<<bitset<5>(all[j])<<","<<bitset<5>(all[k])<<endl;
62                             dp[i][k]=max(dp[i-1][j]+cal(i,all[k]),dp[i][k]);
63                             ans=max(ans,dp[i][k]);
64                         }
65                     }
66                 }
67             }
68         }
69         printf("%d
",ans);
70     }
71 }
在线算……3588ms
#include<bits/stdc++.h>
using namespace std;
int arr[25][25];
vector<int> all;//存储所有可能的状态
int n;
int val[25][1<<15];//预处理每行在各个状态的和
int dp[22][1<<15];
bool check(int state)
{
    while(state)
    {
        if(state&1&&state&2) return false;
        state>>=1;
    }
    return true;
}
int cal(int r,int state)//计算各行和
{
    int ret=0;
    for(int i=n-1;i>=0;i--)
    {
        if(state&1)
            ret+=arr[r][i];
        state>>=1;
    }
    return ret;
}
void db()//预处理出状态和各行和
{
    all.clear();
    int cnt=0;
    for(int i=0;i<(1<<n);i++)
    {
        if(check(i))
        {
            for(int j=0;j<n;j++)
            {
                val[j][cnt]=cal(j,i);
            }
            all.push_back(i);
            cnt++;
        }
    }
}
int main()
{
    while(~scanf("%d",&n))
    {
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
                scanf("%d",&arr[i][j]);
        db();
        memset(dp,0,sizeof(dp));
        int ans=0;
        for(int i=0;i<n;i++)//当前行
        {
            for(int j=0;j<all.size();j++)//枚举上一行状态
            {
                if(i==0)
                {
                    dp[i][j]=val[i][j];
                    ans=max(ans,dp[i][j]);
                    //cout<<bitset<3>(all[j])<<","<<dp[i][all[j]]<<endl;
                }
                else{
                    for(int k=0;k<all.size();k++)//枚举当前行状态
                    {
                        if(!(all[j]&all[k]))
                        {
                            //cout<<bitset<5>(all[j])<<","<<bitset<5>(all[k])<<endl;
                            dp[i][k]=max(dp[i-1][j]+val[i][k],dp[i][k]);
                            ans=max(ans,dp[i][k]);
                        }
                    }
                }
            }
        }
        printf("%d
",ans);
    }
}
原文地址:https://www.cnblogs.com/computer-luo/p/10071914.html