Codeforces 1209E2 Rotate Columns

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

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据