zoukankan      html  css  js  c++  java
  • 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);
        }
    }
  • 相关阅读:
    洛谷 P6599 「EZEC-2」异或 题解
    线段树懒标记以及标记永久化的两种实现方法(模板)
    洛谷P3834 【模板】可持久化线段树 1 题解
    Codeforces 1012B Chemical table (思维+二分图)
    HDU 6386 Age of Moyu (最短路+set)
    CodeForces 739B Alyona and a tree (二分+树上差分)
    Codeforces 444C DZY Loves Colors (线段树)
    HDU 5441 Travel (离线dsu)
    Codeforces 1000E We Need More Bosses (边双连通+最长链)
    CodeForces 219D Choosing Capital for Treeland (树形DP)经典
  • 原文地址:https://www.cnblogs.com/computer-luo/p/10071914.html
Copyright © 2011-2022 走看看