Codeforces Round #584 - Dasha Code Championship E2.Rotate Columns(hard version)(状压dp+子集dp)

题目

T(T<=40)组样例,每次给出一个n*m(n<=12,m<=2000)的矩阵a[],aij的权值为[1,1e5]的整数,

对每一列,你都可以选择对该列进行任意次(含0次)的循环移位,所有列都操作完后,

对于移位后的矩阵,取每一行的最大值ri,对其求和,

求和的最大值

思路来源

https://blog.csdn.net/m0_37809890/article/details/101012876?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.nonecase

https://blog.csdn.net/weixin_44231553/article/details/100859873

题解

把列按列的最大值排降序,

设n<=m,显然最大值之和不会小于此时独立含最大值的n列,

因为如果选取了后面的列的某元素,不如换成前面的独立列更优

 

所以考虑只对前n列作状压dp,dp[i][j]表示前i列覆盖状态为j时的最大值,规模是dp[n][1<<n]的

加入第i行的贡献的时候,枚举j的子集S,把S分给第i行的mx[i][S],从dp[i-1][j^S]转移即可

枚举第i行,枚举子集状态,复杂度是O(n*3^{n})

 

mx[i][S]表示第i行状态为S时的最大值,

可以通过枚举一维行i,枚举循环移位量k,通过k重新求一下数组,

S从小到大通过lowbit O(1)求和转移,复杂度是O(n*n*2^{n})的,

考虑下排序的时间,再乘个T,足矣

 

为了统一n>m的情形,

只需把最后需要状压的列的上界设成min(n,m)即可

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=12,M=1<<12,Q=2e3+5;
//dp[i][j]:前i列state为j时的最大sum 枚举子集
//mx[i][j]:第i列state为j时的和
//p:下标用于排序 big:列的最大值
//b tmp:用于确定偏移量固定时 当前列子集的最大值
int t,n,m,v,dp[N+1][M],mx[N][M],p[Q],big[Q],b[M],tmp[M];
vector<int>a[Q];
int main(){
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&m);
        for(int j=0;j<m;++j){
            a[j].clear();
            p[j]=j;
            big[j]=-1;
        }
        for(int i=0;i<n;++i){
            for(int j=0;j<m;++j){
                scanf("%d",&v);
                a[j].push_back(v);
                big[j]=max(big[j],v);
            }
        }
        sort(p,p+m,[](int i,int j){return big[i]>big[j];});
        memset(mx,0,sizeof mx);
        memset(dp,0,sizeof dp);
        int up=min(n,m);
        for(int i=0;i<up;++i){//rank
            int col=p[i];//实际列
            for(int k=0;k<n;++k){//偏移量
                for(int l=0;l<n;++l){
                    b[1<<l]=a[col][(l+k)%n];
                }
                tmp[0]=0;
                for(int j=1;j<(1<<n);++j){
                    int lb=j&(-j);
                    tmp[j]=tmp[j^lb]+b[lb];
                    mx[i][j]=max(mx[i][j],tmp[j]);
                }
            }
        }
        for(int i=0;i<up;++i){
            for(int j=1;j<(1<<n);++j){
                for(int S=j;;S=(S-1)&j){//考虑S=j或S=0时 只由一个集合拼成 故均合法
                    dp[i+1][j]=max(dp[i+1][j],dp[i][S]+mx[i][j^S]);
                    if(S==0)break;
                }
            }
        }
        printf("%d\n",dp[up][(1<<n)-1]);
    }
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章