您的位置:首页 > 其它

BZOJ4676: Xor-Mul棋盘

2016-08-12 10:27 162 查看
题目大意:给定一个网格棋盘,每条边有一个权值c,每个点有两个权值a,b,你需要给每个点一个权值d,使得每个点的(a^d)*c的和+每条边的两个端点的d异或起来*c的和最小

首先因为是位运算,所以每一位相互独立,可以分开算

然后就能想到状压DP,因为n只有5而且每一列只和上一列有关系,所以可以设f[i][j]表示第i列的状态为j,目前产生的代价最小值

这样转移的时候枚举上一列的情况,然后计算新产生的权值,时间复杂度是O(位运算的每一位*m*状态数*上一列的状态数*计算对应的权值)=O(20*M*2^n*2^n*n)=O(20NM*4^n)

显然是过不了的

我们考虑优化,我们可以发现计算新的权值这一步挪到外面去做,因为他跟你是哪一位没关系,只需要O(M*4^n)预处理出对于每一列来说这列状态和上一列状态分别是j和k时边产生的代价,再O(NM*2^n)当这一列状态为j时,点产生的代价,这样在枚举每一位时就不需要O(N)计算新产生的权值了!

总时间复杂度降成了O(20M*4^N)

#pragma GCC optimize("O2")
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 10010
using namespace std;
long long n,m;
long long a[6]
,b[6]
,c1[6]
,c2[6]
;
long long A[6]
;
long long f
[32],F
[32][32];
bool p(long long x,long long k)
{
return ((x&(1<<(k-1)))!=0);
}
long long solve(long long x)
{
long long i,j,k,l;
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
{
if(a[i][j]&x) A[i][j]=1;
else A[i][j]=0;
}
for(i=1;i<=m;i++)
for(k=0;k<(1<<n);k++)
{
long long tmp=0;
for(j=1;j<=n;j++)
{
tmp+=b[j][i]*(A[j][i]^p(k,j));
if(j==n)
tmp+=c2[j][i]*(p(k,n)^p(k,1));
else
tmp+=c2[j][i]*(p(k,j)^p(k,j+1));
}
f[i][k]=1e18;
//if(k==3) cout<<tmp;
for(l=0;l<(1<<n);l++)
{
long long tmp2=0;
f[i][k]=min(f[i][k],f[i-1][l]+F[i][k][l]);
}
f[i][k]+=tmp;
/* if(k==3)
cout<<i<<' '<<k<<' '<<f[i][k]<<endl;*/
}
long long minn=1e18;
for(i=0;i<(1<<n);i++)
minn=min(minn,f[m][i]);
return minn*x;
}
int main()
{
scanf("%lld%lld",&n,&m);
long long i,j,k,l;
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
scanf("%lld",&a[i][j]);
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
scanf("%lld",&b[i][j]);
for(i=1;i<=n;i++)
for(j=1;j<m;j++)
scanf("%lld",&c1[i][j]);
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
scanf("%lld",&c2[i][j]);
for(i=1;i<=m;i++)
for(k=0;k<(1<<n);k++)
{
long long tmp=0;
for(l=0;l<(1<<n);l++)
{
long long tmp2=0;
for(j=1;j<=n;j++)
tmp2+=c1[j][i-1]*(p(k,j)^p(l,j));
F[i][k][l]=tmp2;
}
}
long long ans=0;
for(i=0;i<=19;i++)
ans+=solve(1<<i);
printf("%lld",ans);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: