@codeforces - 1209E2@ Rotate Columns (hard version)

目录

  • @description@
  • @solution@
  • @accepted code@
  • @details@

@description@

给定一个 n*m 的矩阵 A。
定义一次操作为将矩阵的某一列竖着循环移位,你可以对任意列做任意次操作。
定义 ri 为第 i 行的最大值,最大化 r1 + r2 + ... + rn。

Input
第一行一个整数 t (1≤t≤40),表示数据组数。
每组数据第一行包含两个整数 n m (1≤n≤12,1≤m≤2000) 表示 A 矩阵的行数与列数。
接下来 n 行每行 m 个整数,表示 A 中的元素。(1≤ai,j≤10^5)。

Output
输出 t 个整数:每组数据的答案。

Example
Input
3
2 3
2 5 7
4 2 4
3 6
4 1 5 2 10 4
8 6 6 4 9 10
5 4 9 5 8 7
3 3
9 9 9
1 1 1
1 1 1
Output
12
29
27

Note
第一组数据,只需要将第三行进行操作,得到 r1 = 5, r2 = 7。
第二组数据,不需要进行任何操作,得到 r1 = r2 = 10, r3 = 9。

@solution@

n <= 12 暗示你状压。

每一行的最大值之和最大,其实可以理解为每一行任意选择一个数使它们的和最大。
于是我们可以不考虑元素是不是该行最大值。

定义 dp[i][s] 表示前 i 列已经被选择的行构成集合 s,显然可以滚动数组。
通过枚举第 i + 1 列循环移位的次数,以及第 i + 1 列选择哪些元素计入答案(枚举子集)就可以转移。
但是时间复杂度 O(t*3^n*n*m) 过不了。

一个没什么用的优化:在确定循环移位次数后,我们不枚举子集,而是逐个加入元素。这样就可以将 O(3^n) 降为 O(2^n*n)。
但是时间复杂度 O(t*2^n*n^2*m) 还是过不了,不过因为代码是这么写的所以提一下。

问题最关键的点在于,n 和 m 不是等阶的。
注意到当 m >= n 时,我们总是存在一个方案:选 n 列,使得这 n 列的最大值之和最大。
其实就是给 m 列按照每一列的最大值从大到小排序,取前 n 列。

可以证明只需要这 n 列就可以组合出最终答案。
假如你选择了这 n 列以外的列,共选出 k 行。那么这 n 列中就有 k 列没选到,可以将这 k 列的最大值去替换 n 列以外的 k 行。

于是时间复杂度降为 O(t*2^n*n^3 + T(sort))。

@accepted code@

#include
#include
using namespace std;
const int MAXN = 12;
const int MAXM = 2000;
int a[MAXN + 5][MAXM + 5], n, m;
int f[1< b.key;
    }
}arr[MAXM + 5];
bool tag[MAXM + 5];
void solve() {
    scanf("%d%d", &n, &m);
    for(int j=1;j<=m;j++)
        tag[j] = false, arr[j].key = 0, arr[j].id = j;
    for(int i=0;i

@details@

感觉看完题解都不能理解昨天晚上在干什么。。。

这种通过分析最终答案的下界,以此压缩状态的题虽然比较少见,但是见过的。。。
果然还是我太菜。。。

你可能感兴趣的:(@codeforces - 1209E2@ Rotate Columns (hard version))