题解 CF1209E2 【Rotate Columns (hard version)】

本文讨论了如何在处理大规模矩阵时,通过优化列选择和使用状压DP算法,减少无效列的计算,从(n)列中找到真正有用的(min(n,m))列,以降低(O(n^3))的时间复杂度至(O(3^nn+2^nn^3))。核心内容涉及列最大值排序、动态规划状态定义及转移过程的改进。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

行很少,列很多。

最终的答案只跟每一行有关,所以有些列是没用的。

或者换个角度理解,我们按一列上的最大数将这 \(m\) 列排序,把第一列上的最大数放在第 1 行,第二列上的最大数房子第 2 行,……,把第 \(n\) 列上的最大数放在第 \(n\) 行。于是我们发现第 \(n+1\sim m\) 列甚至连最大数都是没用的,况且前 \(n\) 列不仅会用到最大数,其它数也都有用到的可能,所以可能有用的列数只有 \(\min(n,m)\) 条。

接下来就考虑状压 DP,考虑状态 \(f_{i,j}\) 表示处理到排序后的第 \(i\) 列,行上的状态为 \(j\)(1 表示已经确定了最大数,0 表示没有确定最大数)中已经确定最大数的最大和。

考虑转移,枚举子集,并计算子集的最大贡献,复杂度为 \(O(3^nn^3)\) 但这是无法接受的。

我们发现瓶颈在于计算子集的最大贡献需要 \(O(n^2)\) 的复杂度,而一个子集可能会被计算多次,于是考虑提前预处理,得到复杂度为 \(O(3^n n+2^nn^3)\)

代码:

#include<bits/stdc++.h>
#define log(a) cerr<<"\033[32m[DEBUG] "<<#a<<'='<<(a)<<" @ line "<<__LINE__<<"\033[0m"<<endl
#define LL long long
#define SZ(x) ((int)x.size()-1)
#define ms(a,b) memset(a,b,sizeof a)
#define F(i,a,b) for(int i=(a);i<=(b);++i)
#define DF(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;
inline int read(){char ch=getchar(); int w=1,c=0;
	for(;!isdigit(ch);ch=getchar()) if (ch=='-') w=-1;
	for(;isdigit(ch);ch=getchar()) c=(c<<1)+(c<<3)+(ch^48);
	return w*c;
}
template<typename T>inline void write(T x){
    unsigned long long y=0;T l=0;
    if(x<0)x=-x,putchar(45);
    if(!x){putchar(48);return;}
    while(x){y=y*10+x%10;x/=10;l++;}
    while(l){putchar(y%10+48);y/=10;l--;}
}
template<typename T>inline void writeln(T x){write(x);puts("");}
template<typename T>inline void writes(T x){write(x);putchar(32);}
const int N=13,M=2010,K=(1<<N)+10;
int f[N][K],a[N][M],t[K];
struct Column{
	int maxn,id;
	inline bool operator<(const Column&t)const{
		return maxn>t.maxn;
	}
}c[M];
signed main(){
	int _=read();while(_--){
		int n=read(),m=read();
		F(i,1,m)c[i].id=i,c[i].maxn=0;
		F(i,1,n)
			F(j,1,m){
				a[i][j]=read();
				c[j].maxn=max(c[j].maxn,a[i][j]);
			}
		sort(c+1,c+m+1);
		F(i,1,min(n,m)){
			F(j,0,(1<<n)-1){
				t[j]=0;
				F(k,0,n-1){
					int sum=0;
					F(l,0,n-1)
						if((j>>l)&1)sum+=a[(l+k)%n+1][c[i].id];
					t[j]=max(t[j],sum);
				}
			}
			F(j,0,(1<<n)-1){
				f[i][j]=f[i-1][j];
				for(int k=j;k;k=(k-1)&j)
					f[i][j]=max(f[i][j],f[i-1][j^k]+t[k]);
			}
		}writeln(f[min(n,m)][(1<<n)-1]);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值