习题:Rotate Columns (hard version)(状压DP)
题目
思路
想到DP状态的定义应该不难
设\(dp[i][j]\)为前i列,已经确定了最大值的状态为j
注意我们并不需要知道每一行的最大值具体是什么
预处理一个数组\(temp[i][j]\)表示第i行,如果选的数为j的状态,所能到达的和的最大值
用\(temp\)就可以转移\(dp\)了
注意转移的时候要用到子集枚举的技巧,从\(O(2^{2n})降低到O(3^n)\)
这里阐释一点,如果i-1列的某一行我们已经确定了最大值,
在转移到i的时候,我们并不需要考虑我们位移之后的数是否是大于原来的最大值的,
如果位移之后的数是大于原来的最大值,那么原来的DP值一定会被新的DP值所代替,因为这一位上对于i-1我们既要考虑1,也要考虑0,
代码
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int t;
int n,m;
int la,now;
long long dp[2][(1<<12)],temp[(1<<12)];
struct node
{
int a[13],maxx;
void init()
{
maxx=0;
for(int i=0;i<n;i++)
maxx=max(maxx,a[i]);
}
friend bool operator < (const node &a,const node &b)
{
return a.maxx>b.maxx;
}
}a[1005];
void c_in()
{
cin>>n>>m;
for(int i=0;i<n;i++)
for(int j=1;j<=m;j++)
cin>>a[j].a[i];
for(int i=1;i<=m;i++)
a[i].init();
sort(a+1,a+m+1);
m=min(n,m);
la=0;
now=1;
memset(dp,0,sizeof(dp));
for(int j=1;j<=m;j++)
{
la=now;
now^=1;
memset(dp[now],0,sizeof(dp[now]));
memset(temp,0,sizeof(temp));
for(int i=0;i<(1<<n);i++)
{
for(int k=0;k<n;k++)
{
long long ret=0;
for(int z=0;z<n;z++)
if(i&(1<<z))
ret=ret+a[j].a[(z+k)%n];
temp[i]=max(temp[i],ret);
}
}
for(int i=0;i<(1<<n);i++)
for(int j=i;j<(1<<n);j=(j+1)|i)
dp[now][j]=max(dp[now][j],dp[la][i]+temp[j^i]);
}
cout<<dp[now][(1<<n)-1]<<endl;
}
int main()
{
ios::sync_with_stdio(false);
cin>>t;
for(int i=1;i<=t;i++)
c_in();
return 0;
}

浙公网安备 33010602011771号