当前位置: 代码迷 >> 综合 >> CF1209E2 Rotate Columns (hard version)
  详细解决方案

CF1209E2 Rotate Columns (hard version)

热度:25   发布时间:2024-02-08 19:51:59.0

一、题目

点此看题

二、解法

首先可以缩小数据范围,你会发现如果将每一列按最大值排序,那么 m m 较大时取前 n n 列算答案即可。

要将题目求的东西做一个转化,每一行的最大值转化成每一行至多选一个数(反正要求总和最大),这样就一定会选每一行的最大值。设 d p [ i ] [ s ] dp[i][s] 为处理到了第 i i 列,行是否选了数的状压为 s s

转移要预处理算出旋转情况下的子集总和最大值,这一列的一个状态 j j 旋转 d d 次得到的状态是((j>>d)|(j<<n-d))&((1<<n)-1),那么在 d d [ 0 , n ) [0,n) 时找最大值即可。转移过程枚举子集的子集,所以时间复杂度 O ( t n 3 n ) O(tn3^n)

#include <cstdio>
#include <cstring> 
#include <iostream>
#include <algorithm>
using namespace std;
const int M = 2005;
int read()
{int x=0,f=1;char c;while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}return x*f;
}
int T,n,m,dp[20][1<<12],sum[1<<12];
struct node
{int a[20],mx;bool operator < (const node &b) const{return mx>b.mx;}
}s[M];//下标是列
signed main()
{T=read();while(T--){n=read();m=read();for(int j=1;j<=m;j++)s[j].mx=0;for(int i=0;i<n;i++)for(int j=1;j<=m;j++){s[j].a[i]=read();s[j].mx=max(s[j].mx,s[j].a[i]);}sort(s+1,s+m+1);memset(dp,0,sizeof dp);for(int i=1;i<=min(n,m);i++){memset(sum,0,sizeof sum);for(int j=1;j<(1<<n);j++)for(int k=0;k<n;k++)if(j&(1<<k))sum[j]=sum[j^(1<<k)]+s[i].a[k];for(int j=1;j<(1<<n);j++)for(int d=0;d<n;d++){int k=((j>>d)|(j<<n-d))&((1<<n)-1);sum[j]=max(sum[j],sum[k]);}for(int j=1;j<(1<<n);j++)for(int k=j;;k=(k-1)&j){dp[i][j]=max(dp[i][j],dp[i-1][k^j]+sum[k]);if(k==0) break;}}printf("%d\n",dp[min(n,m)][(1<<n)-1]);}
}
  相关解决方案