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);
        }
    }
  • 相关阅读:
    trackr: An AngularJS app with a Java 8 backend – Part III
    trackr: An AngularJS app with a Java 8 backend – Part II
    21. Wireless tools (无线工具 5个)
    20. Web proxies (网页代理 4个)
    19. Rootkit detectors (隐形工具包检测器 5个)
    18. Fuzzers (模糊测试器 4个)
    16. Antimalware (反病毒 3个)
    17. Debuggers (调试器 5个)
    15. Password auditing (密码审核 12个)
    14. Encryption tools (加密工具 8个)
  • 原文地址:https://www.cnblogs.com/computer-luo/p/10071914.html
Copyright © 2011-2022 走看看