Codeforces 1209E2 Rotate Columns
题目链接
https://codeforces.com/problemset/problem/1209/E2
题目大意
给你一个 $n \times m$ 的矩阵,你可以将每一列中的数循环排列,问每一行的最大值的和最大为多少。
题目分析
看到 $n \leq 12$ 的条件立马想到状压 $DP$。令 $F[S]$ 表示集合 $S$ 中的行最大值都已经被确定。那么显然可以对于每一列都求出每种集合在这一列的 $n$ 种排列方法下的最大值。那么可以通过按列添加枚举子集来进行 $DP$。这时复杂度是 $O(Tm(n2^n+3^n))$。显然有点大,只能通过 $n\leq 4$ 的 $E1$。如果把 $m$ 列都跑了显然有很多列其实是一个元素都用不上的。如果那把这些列想办法删掉就可以减小复杂度。考虑我最多只需要前 $n$ 大的数。如果将所有的列按照该列的最大值排序,那么其实只需要跑最大的 $n$ 列就可以了。这样一来复杂度就变为 $O(Tn(n2^n+3^n))$ ,就可以通过这道题了。
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2000 + 5;
int t;
int n, m;
int a[20][maxn];
int b[50];
int c[maxn];
int maxx[maxn];
int f[15][(1<<13)];
int g[(1<<15)];
bool cmp(int x, int y){
return maxx[x] > maxx[y];
}
int solve(int x){
int i, j, k;
memset(g, 0, sizeof(g));
for(i=1;i<=n;i++){
b[i] = a[i][x];
}
for(i=n+1;i<=2*n;i++){
b[i] = a[i - n][x];
}
for(i=0;i<(1<<n);i++){
for(j=0;j<n;j++){
int ans = 0;
for(k=1;k<=n;k++){
if((i >> (k - 1)) & 1){
ans += b[j + k];
}
}
g[i] = max(g[i], ans);
}
}
return 0;
}
int main(){
int i, j, k;
scanf("%d", &t);
while(t--){
scanf("%d%d", &n, &m);
memset(maxx, 0, sizeof(maxx));
memset(f, 0, sizeof(f));
for(i=1;i<=n;i++){
for(j=1;j<=m;j++){
scanf("%d", &a[i][j]);
maxx[j] = max(maxx[j], a[i][j]);
c[j] = j;
}
}
sort(c + 1, c + m + 1, cmp);
for(i=1;i<=min(n,m);i++){
solve(c[i]);
for(j=0;j<(1<<n);j++){
f[i][j] = max(f[i][j], f[i - 1][j]);
for(k=j;k>0;k=((k-1)&j)){
f[i][j] = max(f[i][j], f[i - 1][j ^ k] + g[k]);
}
}
}
printf("%d\n", f[min(n, m)][(1<<n)-1]);
}
return 0;
}