題目
T(T<=40)組樣例,每次給出一個n*m(n<=12,m<=2000)的矩陣a[],aij的權值爲[1,1e5]的整數,
對每一列,你都可以選擇對該列進行任意次(含0次)的循環移位,所有列都操作完後,
對於移位後的矩陣,取每一行的最大值ri,對其求和,
求和的最大值
思路來源
https://blog.csdn.net/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行,枚舉子集狀態,複雜度是
mx[i][S]表示第i行狀態爲S時的最大值,
可以通過枚舉一維行i,枚舉循環移位量k,通過k重新求一下數組,
S從小到大通過lowbit O(1)求和轉移,複雜度是的,
考慮下排序的時間,再乘個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;
}