Codeforces Round #584 - Dasha Code Championship E2.Rotate Columns(hard version)(状压dp+子集dp)

本文探讨了一种针对矩阵的循环移位优化算法,通过对矩阵列的最大值进行降序排列,利用状压DP方法,计算在对每列进行任意次循环移位后,取每行最大值并求和的最大值问题。文章详细介绍了算法的实现思路,包括排序、子集枚举和状态转移等关键步骤,并提供了完整的C++代码实现。

题目

T(T<=40)组样例,每次给出一个n*m(n<=12,m<=2000)的矩阵a[],aij的权值为[1,1e5]的整数,

对每一列,你都可以选择对该列进行任意次(含0次)的循环移位,所有列都操作完后,

对于移位后的矩阵,取每一行的最大值ri,对其求和,

求和的最大值

思路来源

https://blog.youkuaiyun.com/m0_37809890/article/details/101012876?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.nonecase

https://blog.youkuaiyun.com/weixin_44231553/article/details/100859873

题解

把列按列的最大值排降序,

设n<=m,显然最大值之和不会小于此时独立含最大值的n列,

因为如果选取了后面的列的某元素,不如换成前面的独立列更优

 

所以考虑只对前n列作状压dp,dp[i][j]表示前i列覆盖状态为j时的最大值,规模是dp[n][1<<n]的

加入第i行的贡献的时候,枚举j的子集S,把S分给第i行的mx[i][S],从dp[i-1][j^S]转移即可

枚举第i行,枚举子集状态,复杂度是O(n*3^{n})

 

mx[i][S]表示第i行状态为S时的最大值,

可以通过枚举一维行i,枚举循环移位量k,通过k重新求一下数组,

S从小到大通过lowbit O(1)求和转移,复杂度是O(n*n*2^{n})的,

考虑下排序的时间,再乘个T,足矣

 

为了统一n>m的情形,

只需把最后需要状压的列的上界设成min(n,m)即可

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=12,M=1<<12,Q=2e3+5;
//dp[i][j]:前i列state为j时的最大sum 枚举子集
//mx[i][j]:第i列state为j时的和
//p:下标用于排序 big:列的最大值
//b tmp:用于确定偏移量固定时 当前列子集的最大值
int t,n,m,v,dp[N+1][M],mx[N][M],p[Q],big[Q],b[M],tmp[M];
vector<int>a[Q];
int main(){
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&m);
        for(int j=0;j<m;++j){
            a[j].clear();
            p[j]=j;
            big[j]=-1;
        }
        for(int i=0;i<n;++i){
            for(int j=0;j<m;++j){
                scanf("%d",&v);
                a[j].push_back(v);
                big[j]=max(big[j],v);
            }
        }
        sort(p,p+m,[](int i,int j){return big[i]>big[j];});
        memset(mx,0,sizeof mx);
        memset(dp,0,sizeof dp);
        int up=min(n,m);
        for(int i=0;i<up;++i){//rank
            int col=p[i];//实际列
            for(int k=0;k<n;++k){//偏移量
                for(int l=0;l<n;++l){
                    b[1<<l]=a[col][(l+k)%n];
                }
                tmp[0]=0;
                for(int j=1;j<(1<<n);++j){
                    int lb=j&(-j);
                    tmp[j]=tmp[j^lb]+b[lb];
                    mx[i][j]=max(mx[i][j],tmp[j]);
                }
            }
        }
        for(int i=0;i<up;++i){
            for(int j=1;j<(1<<n);++j){
                for(int S=j;;S=(S-1)&j){//考虑S=j或S=0时 只由一个集合拼成 故均合法
                    dp[i+1][j]=max(dp[i+1][j],dp[i][S]+mx[i][j^S]);
                    if(S==0)break;
                }
            }
        }
        printf("%d\n",dp[up][(1<<n)-1]);
    }
    return 0;
}

 

下载前可以先看下教程 https://pan.quark.cn/s/a4b39357ea24 在网页构建过程中,表单(Form)扮演着用户与网站之间沟通的关键角色,其主要功能在于汇集用户的各类输入信息。 JavaScript作为网页开发的核心技术,提供了多样化的API和函数来操作表单组件,诸如input和select等元素。 本专题将详细研究如何借助原生JavaScript对form表单进行视觉优化,并对input输入框与select下拉框进行功能增强。 一、表单基础1. 表单组件:在HTML语言中,<form>标签用于构建一个表单,该标签内部可以容纳多种表单组件,包括<input>(输入框)、<select>(下拉框)、<textarea>(多行文本输入区域)等。 2. 表单参数:诸如action(表单提交的地址)、method(表单提交的协议,为GET或POST)等属性,它们决定了表单的行为特性。 3. 表单行为:诸如onsubmit(表单提交时触发的动作)、onchange(表单元素值变更时触发的动作)等事件,能够通过JavaScript进行响应式处理。 二、input元素视觉优化1. CSS定制:通过设定input元素的CSS属性,例如border(边框)、background-color(背景色)、padding(内边距)、font-size(字体大小)等,能够调整其视觉表现。 2. placeholder特性:提供预填的提示文字,以帮助用户明确输入框的预期用途。 3. 图标集成:借助:before和:after伪元素或者额外的HTML组件结合CSS定位技术,可以在输入框中嵌入图标,从而增强视觉吸引力。 三、select下拉框视觉优化1. 复选功能:通过设置multiple属性...
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小衣同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值