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;
}

 

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