BZOJ4676: Xor-Mul棋盘

26 篇文章 0 订阅
2 篇文章 0 订阅

题目大意:给定一个网格棋盘,每条边有一个权值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][N],b[6][N],c1[6][N],c2[6][N];
long long A[6][N];
long long f[N][32],F[N][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);
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值