zoukankan      html  css  js  c++  java
  • @codeforces


    @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<cstdio>
    #include<algorithm>
    using namespace std;
    const int MAXN = 12;
    const int MAXM = 2000;
    int a[MAXN + 5][MAXM + 5], n, m;
    int f[1<<MAXN], g[1<<MAXN], h[1<<MAXN], lg[1<<MAXN];
    int lowbit(int x) {return x & -x;}
    struct node{
    	int key, id;
    	friend bool operator < (node a, node b) {
    		return a.key > 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<n;i++)
    		for(int j=1;j<=m;j++)
    			scanf("%d", &a[i][j]), arr[j].key = max(arr[j].key, a[i][j]);
    	sort(arr + 1, arr + m + 1);
    	for(int i=1;i<=m&&i<=n;i++)
    		tag[arr[i].id] = true;
    	int tot = (1<<n);
    	for(int j=0;j<tot;j++) f[j] = 0;
    	for(int j=0;j<n;j++) lg[1<<j] = j;
    	for(int i=1;i<=m;i++) {
    		if( !tag[i] ) continue;
    		for(int j=0;j<tot;j++) h[j] = 0;
    		for(int j=1;j<=n;j++) {
    			for(int k=0;k<tot;k++)
    				g[k] = f[k];
    			for(int k=0;k<tot;k++) {
    				int p = (tot - 1)^k;
    				while( p ) {
    					int x = lowbit(p), y = lg[x];
    					g[k|x] = max(g[k|x], g[k] + a[y][i]);
    					p -= x;
    				}
    			}
    			int tmp = a[0][i];
    			for(int k=0;k<n-1;k++)
    				a[k][i] = a[k + 1][i];
    			a[n - 1][i] = tmp;
    			for(int j=0;j<tot;j++)
    				h[j] = max(h[j], g[j]), g[j] = 0;
    		}
    		for(int j=0;j<tot;j++) f[j] = h[j];
    	}
    	printf("%d
    ", f[tot - 1]);
    }
    int main() {
    	int t; scanf("%d", &t);
    	while( t-- ) solve();
    }
    

    @details@

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

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

  • 相关阅读:
    网速测速结果,单位换算
    js实现多个列表分别倒计时功能
    微信小程序使用wxs在页面中调用js函数,颜色值格式转换 rgb和十六进制的转换
    css样式列表宽度自适应布局
    投资轮次说明
    Ajax与JSON共同使用的小实例
    js结构
    关于vue的小实例
    label与input之间的对应
    购物demo
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/11521194.html
Copyright © 2011-2022 走看看