CF1209E2 Rotate Columns (hard version)
题意
给定一个 \(n \times m\) 的矩阵,你可以对每一列进行若干次循环移位。
求操作完成后每一行的最大值之和最大是多少。
Solution
考虑到 \(n\) 的范围比较小,应该可以用状压。于是令 \(dp_{i,s}\) 表示处理完前 \(i\) 列,使 \(s\) 集合中的行的最大值已经确定的最大值和最大是多少。那么对于每一列相当于可以钦定某些行的最大值确定。
每次枚举子集然后循环位移当前列即可。这样可能会枚举到不可能的情况,但是肯定不优于最后答案,所以没关系。
然后这样复杂度就爆炸了。
然后贪心的想,最优策略肯定是每一列取其最大值就好。于是对每一列按其最大值排序,然后取前 \(n\) 个做就行了。
Code
// Problem: CF1209E2 Rotate Columns (hard version)
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/CF1209E2
// Memory Limit: 2500 MB
// Time Limit: 250000 ms
#include<bits/stdc++.h>
#define ll long long
#define inf (1<<30)
#define INF (1ll<<60)
#define pb emplace_back
#define pii pair<int,int>
#define mkp make_pair
#define fi first
#define se second
#define all(a) a.begin(),a.end()
#define siz(a) (int)a.size()
#define clr(a) memset(a,0,sizeof(a))
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
#define per(i,j,k) for(int i=(j);i>=(k);i--)
#define pt(a) cerr<<#a<<'='<<a<<' '
#define pts(a) cerr<<#a<<'='<<a<<'\n'
// #define int long long
using namespace std;
const int MAXN=2010;
int n,m;
struct Col{
int a[15],mx;
bool friend operator<(Col x,Col y){
return x.mx>y.mx;
}
}c[MAXN];
int dp[15][(1<<12)+5],mxc[(1<<12)+5];
void solve(){
cin>>n>>m;
rep(j,1,m) c[j].mx=0;
rep(i,1,n) rep(j,1,m){
cin>>c[j].a[i];
c[j].mx=max(c[j].mx,c[j].a[i]);
}sort(c+1,c+1+m);
memset(dp,0,sizeof(dp));
rep(i,1,min(n,m)){
rep(j,0,(1<<n)-1){
mxc[j]=0;
rep(dlt,0,n-1){
int sum=0;
rep(k,1,n) if(j&(1<<(k-1))) sum+=c[i].a[(k+dlt-1)%n+1];
mxc[j]=max(mxc[j],sum);
}
}
rep(j,0,(1<<n)-1){
dp[i][j]=max(dp[i][j],dp[i-1][0]+mxc[j]);
for(int sj=j;sj;sj=(sj-1)&j){
int st=j^sj;
dp[i][j]=max(dp[i][j],dp[i-1][sj]+mxc[st]);
}
}
}cout<<dp[min(n,m)][(1<<n)-1]<<'\n';
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int T;for(cin>>T;T--;)solve();
return 0;
}