【题解】CF1209E2 Rotate Columns (hard version)
【CF1209E2】题解
一:【题意】
给定一个n*m的矩阵,每列可以循环移位任意次
记r[i]为第i行最大值,求r[1]+r[2]+...+r[n]最大值
二:【解法】
注意到,只有最大值最大的前n列会对结果造成影响,换言之,剩下的m-n列可以抛弃
此时m=n数据范围大大缩小
我们设计dp[i][S]:表示前i列中,S集合的行已经被确定了最大值,此时最大值之和的最大值为dp[i][S]
考虑转移,枚举子集T,枚举移位次数k
dp[i][S]=max(dp[i-1][T]+a[(b+k-1)%n+1][i]),b属于S^T
此时时间复杂度为O(tnn*3^n),会超时
我们考虑在枚举每一列之后,提前预处理位移的贡献
pos[S]表示循环移动任意位时,集合S对应位置的和的最大值
dp[i][S]=max(dp[i-1][T]+pos[S^T])
此时时间复杂度为O(tn3^n)
三:【代码】
#include<bits/stdc++.h>
using namespace std;
const int N=13,M=2010;
int n,m;
int mp[N][N];
//useless
vector<int> a[M];int o[M];
bool cmp(int x,int y){
return a[x].back()>a[y].back();
}
void small(){
for(int i=1;i<=m;i++){
o[i]=i;
int maxn=0;
for(auto v:a[i]) maxn=max(maxn,v);
a[i].push_back(maxn);
}
sort(o+1,o+1+m,cmp);
m=min(m,n);
for(int i=1;i<=m;i++){
for(int j=0;j<n;j++) mp[j+1][i]=a[o[i]][j];
}
}
//useless
int dp[N][1<<N],pos[1<<N];
void clear(){
for(int i=1;i<=m;i++) a[i].clear();
}
void getpos(int tp){
for(int st=0;st<(1<<n);st++){
pos[st]=0;
for(int i=0;i<n;i++){//偏移量
int ans=0;
for(int j=0;st>>j;j++){
if(st>>j&1) ans+=mp[(j+i)%n+1][tp];
}
pos[st]=max(pos[st],ans);
}
}
}
void solve(){
cin>>n>>m;
clear();
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
int k;cin>>k;
a[j].push_back(k);
}
}
small();
for(int i=1;i<=m;i++){
getpos(i);
for(int S=0;S<(1<<n);S++){
dp[i][S]=0;
for(int T=S;;T=(T-1)&S){
dp[i][S]=max(dp[i][S],dp[i-1][T]+pos[S^T]);
if(T==0) break;
}
}
}
cout<<dp[m][(1<<n)-1]<<"\n";
}
int main(){
int t;cin>>t;
while(t--) solve();
return 0;
}

浙公网安备 33010602011771号