codeforces#1290E2 - Rotate Columns (hard version)(子集dp)

题目链接:

https://codeforces.com/contest/1209/problem/E2

题意:

给出$n$行和$m$列

每次操作循环挪动某列一次

可以执行无数次这样的操作

让每行最大值的累加和最大

数据范围:

$1\leq n \leq 12$

$1\leq m \leq 20000$

分析:

定义$dp[i][j]$,考虑前$i$列,选择状态为$j$的最大值

$ans=dp[m][(1<<n)-1]$

$dp[i][j]$可以由$dp[i-1][k]$转移,$k$是$j$的二进制子集

$easy$难度还是比较好写的

$hard$难度需要预处理,和保留最多$n$列

ac代码:

#include<bits/stdc++.h>
#define ll long long
#define pii pair<int,int>
using namespace std;
const int maxn=12+10;
const int maxm=2007;
int num[maxn][maxm];
pii p[maxn*maxm];
int lis[maxn][maxn],cnt;
int dp[maxn][(1<<12)+10],f[maxn][(1<<12)+10];
set<int>se;
int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        int n,m;
        scanf("%d %d",&n,&m);
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                scanf("%d",&num[i][j]);
                p[(i-1)*m+j]=make_pair(num[i][j],j);
            }
        }
         sort(p+1,p+1+n*m);
         se.clear();
         for(int i=0;i<n*m;i++){
             se.insert(p[n*m-i].second);
             if(se.size()==n)break;//保留最多n列
         }
         int cnt=0;
         for(auto i:se){
             cnt++;
             for(int j=1;j<=n;j++)
                 lis[j][cnt]=num[j][i];
         }
         m=cnt;

        int len=(1<<n);
        memset(f,0,sizeof(f));
        for(int i=1;i<=m;i++)//预处理每列选择状态的最优解
            for(int choose=0;choose<len;choose++){

                for(int j=1;j<=n;j++){
                     int res=0;
                    for(int k=1;k<=n;k++){
                        int g=(k-1+j)%n+1;
                        if((1<<(g-1))&choose)res+=lis[k][i];
                    }
                    f[i][choose]=max(res,f[i][choose]);
                }

        }
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=m;i++){
            for(int choose=0;choose<len;choose++){
                for(int shift=choose;;shift=((shift-1)&choose)){//枚举choose的子集
                    int v=choose-(shift&choose);
                    dp[i][choose]=max(dp[i][choose],dp[i-1][shift]+f[i][v]);
                    if(shift==0)break;
                }
            }
        }
        printf("%d\n",dp[m][len-1]);
    }
    return 0;
}

  

原文地址:https://www.cnblogs.com/carcar/p/11544541.html

时间: 2024-10-09 01:28:25

codeforces#1290E2 - Rotate Columns (hard version)(子集dp)的相关文章

@codeforces - [email&#160;protected] Rotate Columns (hard version)

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一个 n*m 的矩阵 A. 定义一次操作为将矩阵的某一列竖着循环移位,你可以对任意列做任意次操作. 定义 ri 为第 i 行的最大值,最大化 r1 + r2 + ... + rn. Input 第一行一个整数 t (1≤t≤40),表示数据组数. 每组数据第一行包含两个整数 n m

Codeforces 235B Let&#39;s Play Osu! (概率dp求期望+公式变形)

B. Let's Play Osu! time limit per test:2 seconds memory limit per test:256 megabytes You're playing a game called Osu! Here's a simplified version of it. There are n clicks in a game. For each click there are two outcomes: correct or bad. Let us deno

codeforces 161D - Distance in Tree(树形dp)

题目大意: 求出树上距离为k的点对有多少个. 思路分析: dp[i][j] 表示 i 的子树中和 i 的距离为 j 的点数有多少个.注意dp[i] [0] 永远是1的. 然后在处理完一颗子树后,就把自身的dp 更新. 更新之前更新答案. 如果这颗子树到 i 有 x 个距离为j的.那么答案就要加上 dp[i] [ k-j-1] * x; #include <iostream> #include <cstdio> #include <cstring> #include &l

CodeForces 258B Little Elephant and Elections 数位DP

前面先用数位DP预处理,然后暴力计算组合方式即可. #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <climits> #include <string> #include <iostream> #include <map> #include <cstdlib> #include

Codeforces 437E The Child and Polygon(区间DP)

题目链接:Codeforces 437E The Child and Polygon 题目大意:给出一个多边形,问说有多少种分割方法,将多边形分割为多个三角形. 解题思路:首先要理解向量叉积的性质,一开始将给出的点转换成顺时针,然后用区间dp计算.dp[i][j]表示从点i到点j可以有dp[i][j]种切割方法.然后点i和点j是否可以做为切割线,要经过判断,即在i和j中选择的话点k的话,点k要在i,j的逆时针方. #include <cstdio> #include <cstring&g

Codeforces 219D. Choosing Capital for Treeland (树dp)

题目链接:http://codeforces.com/contest/219/problem/D 树dp 1 //#pragma comment(linker, "/STACK:102400000, 102400000") 2 #include <algorithm> 3 #include <iostream> 4 #include <cstdlib> 5 #include <cstring> 6 #include <cstdio&

CodeForces Round#229 DIV2 C 递推DP

对这道题目也只好说呵呵了,没注意k的范围最大才10,所以昨晚纠结了很久,没什么好的方法来处理,后来无奈想去翻翻题解,发现人家开头就来了句,因为k的范围比较小 所以.........我只好暂停马上回头看看题目,是的,k比较小所以完全可以先在询问前预处理DP一遍, DP就比较清晰了,dp[i][j]  (i>=0 && i<k,,,,j>=i && j <=n)代表意义呢 以i为开头的  区间[1,j]注意 是 1~j的 所需要的操作数,题目问的是最小操

codeforces 235B Let&#39;s Play Osu! 概率dp

题意:给定n表示有n个格子,下面每个格子为O的概率是多少.对于一段连续 x 个O的价值就是 x^2 ;求获得的价值的期望是多少. 思路:n^2=n×(n-1)+n,设ai为第i段连续O的长度,∑ai^2 = ∑[ ai+ ai*(ai-1) ] = ∑ ai*(ai-1) + ∑ai = ∑ C(ai, 2)*2 + ∑ai,那么问题可以转 化为求长度大于1的连续段数*2+O的个数的总期望. ∑ai我们可以理解为O的总个数,所以它的期望为∑pi: C(ai, 2)*2我们可以认 为是连续ai个O

Codeforces 235B Let&#39;s Play Osu! 概率dp(水

题目链接:点击打开链接 给定n表示有n个格子 下面每个格子为O的概率是多少. 对于一段连续 x 个O的价值就是 x*x ; 问: 获得的价值的期望是多少. 思路: 把公式拆一下.. #include <cstdio> const int N = 100005; double dp[N][2], p[N]; int main(){ int n; scanf("%d", &n); for(int i = 1; i <= n; i ++) { scanf("