题目链接:
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$列
easy代码:
const int N=5;
const int M=105;
int n,m,B[M],A[N][M];
int f[1<<N][M];
int main( )
{
int i,j,k,t,z,r,u,G,T=read( );
while(T--)
{
read(n);
read(m);;
for(i=1; i<=n; i++)
for(j=1; j<=m; j++)
A[i][j]=read( );
int S=(1<<n)-1;
for(i=1; i<=m; i++)
for(j=0; j<=S; j++)
f[j][i]=0;
for(i=1; i<=m; i++)
{
for(j=0; j<=S; j++)
{
for(k=j;; k=(k-1)&j)
{
u=0;
for(t=0; t<n; t++)
{
r=0;
for(z=1; z<=n; z++)
{
G=((z-1)+t)%n+1;
if(((1<<(G-1))&j)&&(!((1<<(G-1))&k)))
r+=A[z][i];
}
u=max(u,r);
}
f[j][i]=max(f[j][i],f[k][i-1]+u);
if(!k) break;
}
}
}
printf("%d\n",f[S][n]);
}
return 0;
}
hard代码:
#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;
}