习题:Rotate Columns (hard version)(状压DP)

题目

传送门

思路

想到DP状态的定义应该不难

\(dp[i][j]\)为前i列,已经确定了最大值的状态为j

注意我们并不需要知道每一行的最大值具体是什么

预处理一个数组\(temp[i][j]\)表示第i行,如果选的数为j的状态,所能到达的和的最大值

\(temp\)就可以转移\(dp\)

注意转移的时候要用到子集枚举的技巧,从\(O(2^{2n})降低到O(3^n)\)

这里阐释一点,如果i-1列的某一行我们已经确定了最大值,

在转移到i的时候,我们并不需要考虑我们位移之后的数是否是大于原来的最大值的,

如果位移之后的数是大于原来的最大值,那么原来的DP值一定会被新的DP值所代替,因为这一位上对于i-1我们既要考虑1,也要考虑0,

代码

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int t;
int n,m;
int la,now;
long long dp[2][(1<<12)],temp[(1<<12)];
struct node
{
    int a[13],maxx;
    void init()
    {
        maxx=0;
        for(int i=0;i<n;i++)
            maxx=max(maxx,a[i]);
    }
    friend bool operator < (const node &a,const node &b)
    {
        return a.maxx>b.maxx;
    }
}a[1005];
void c_in()
{
    cin>>n>>m;
    for(int i=0;i<n;i++)
        for(int j=1;j<=m;j++)
            cin>>a[j].a[i];
    for(int i=1;i<=m;i++)
        a[i].init();
    sort(a+1,a+m+1);
    m=min(n,m);
    la=0;
    now=1;
    memset(dp,0,sizeof(dp));
    for(int j=1;j<=m;j++)
    {
        la=now;
        now^=1;
        memset(dp[now],0,sizeof(dp[now]));
        memset(temp,0,sizeof(temp));
        for(int i=0;i<(1<<n);i++)
        {
            for(int k=0;k<n;k++)
            {
                long long ret=0;
                for(int z=0;z<n;z++)
                    if(i&(1<<z))
                        ret=ret+a[j].a[(z+k)%n];
                temp[i]=max(temp[i],ret);
            }
        }
        for(int i=0;i<(1<<n);i++)
            for(int j=i;j<(1<<n);j=(j+1)|i)
                dp[now][j]=max(dp[now][j],dp[la][i]+temp[j^i]);
    }
    cout<<dp[now][(1<<n)-1]<<endl;
}
int main()
{
    ios::sync_with_stdio(false);
    cin>>t;
    for(int i=1;i<=t;i++)
        c_in();
    return 0;
}
posted @ 2020-08-09 16:09  loney_s  阅读(99)  评论(0)    收藏  举报