zoukankan      html  css  js  c++  java
  • 【BZOJ4676】Xor-Mul棋盘 拆位+状压DP

    【BZOJ4676】Xor-Mul棋盘

    Description

    一个n*m的棋盘,左上角为(1,1),右下角为(n,m)。相邻的2点之间有连边(如下图中实线)特殊地,(1,i)与(n,i)也连有一条边(如下图中虚线),i=1..m。
    如下图,就是一个n=3,m=4的棋盘。
    每个点(i,j)给定2个值a[i][j],b[i][j]。每条边e给定1个值c[e]。
    你的任务是给每一个点一个非负的d值,最小化(S1+S2)。

    Input

    第一行2个整数n,m。
    接着n行,每行m个数,其中第i行第j个数表示a[i][j]。
    接着n行,每行m个数,其中第i行第j个数表示b[i][j]。
    接着n行,每行m-1个数,其中第i行第j个数表示(i,j)与(i,j+1)的边的c值。
    接着n-1行,每行m个数,其中第i行第j个数表示(i,j)与(i+1,j)的边的c值。
    最后一行m个数,其中第i个数表示(1,i)与(n,i)的边的c值。
    2<=n<=5, 1<=m<=10000
    a,b,c 的值均为不大于 10^6 的正整数

    Output

    一个整数,表示S1+S2的最小值

    Sample Input

    2 2
    3 6
    7 3
    9 9
    1 8
    9 5
    3 9
    4 3

    Sample Output

    49

    题解:首先拆位是显然的。由于n只有5,考虑状压。

    设f[i][j][k]表示第i列的第j位状态为k的最小值,那么我们先计算同列之间产生的贡献,然后计算相邻列之间产生的贡献。我们可以O(n)求出同列的点的贡献,然后$O(2^{2n})$枚举当前列和上一列的状态,并希望O(1)时间得到这两个状态合在一起的价值。这个可以预处理出val[S]数组表示当前列和上一列的异或值为S时产生的贡献,然后就能转移了。

    最终复杂度O(m*20*2^{2n}),居然也能过~

     

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    using namespace std;
    typedef long long ll;
    int n,m;
    ll ans;
    ll A[7][10010],B[7][10010],C1[7][10010],C2[7][10010],f[2][21][1025],val[1025];
    inline int rd()
    {
    	int ret=0,f=1;	char gc=getchar();
    	while(gc<'0'||gc>'9')	{if(gc=='-')	f=-f;	gc=getchar();}
    	while(gc>='0'&&gc<='9')	ret=ret*10+gc-'0',gc=getchar();
    	return ret*f;
    }
    int main()
    {
    	n=rd(),m=rd();
    	int i,j,d,a,b;
    	ll c;
    	for(i=0;i<n;i++)	for(j=1;j<=m;j++)	A[i][j]=rd();
    	for(i=0;i<n;i++)	for(j=1;j<=m;j++)	B[i][j]=rd();
    	for(i=0;i<n;i++)	for(j=2;j<=m;j++)	C1[i][j]=rd();
    	for(i=0;i<n;i++)	for(j=1;j<=m;j++)	C2[i][j]=rd();
    	for(i=1;i<=m;i++)
    	{
    		d=i&1;
    		memset(f[d],0,sizeof(f[d]));
    		if(i>1)
    		{
    			for(a=0;a<1<<n;a++)
    			{
    				val[a]=0;
    				for(b=0;b<n;b++)	val[a]+=((a>>b)&1)*C1[b][i];
    			}
    		}
    		for(j=0;j<=20;j++)
    		{
    			for(a=0;a<1<<n;a++)
    			{
    				c=0;
    				for(b=0;b<n;b++)
    				{
    					c+=(((a>>b)&1)^((A[b][i]>>j)&1))*B[b][i];
    					c+=(((a>>b)&1)^((a>>((b+1)%n))&1))*C2[b][i];
    				}
    				f[d][j][a]=1ll<<60;
    				if(i>1)	for(b=0;b<1<<n;b++)	f[d][j][a]=min(f[d][j][a],c+val[a^b]+f[d^1][j][b]);
    				else	f[d][j][a]=c;
    			}
    		}
    	}
    	for(j=0;j<=20;j++)
    	{
    		c=1ll<<60;
    		for(i=0;i<1<<n;i++)	c=min(c,f[m&1][j][i]);
    		ans+=c<<j;
    	}
    	printf("%lld",ans);
    	return 0;
    }

     

  • 相关阅读:
    第八周编程总结
    第五周课程总结&试验报告(三)
    第四周课程总结&试验报告(二)
    第三周课程总结&实验报告一
    2019春总结作业
    第一次随笔
    我人生中影响最大的三位老师
    第十二周作业
    第十一周编程总结
    第十周作业
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/7669846.html
Copyright © 2011-2022 走看看