zoukankan      html  css  js  c++  java
  • bzoj5148:[BeiJing2018]Kakuro

    传送门

    有上下界最小费用可行流,行列建边变形。
    行列建边相信大家都做过,没做过的可以看一下这个题:bzoj3698XWW的难题,对应的我写的题解题解
    这个题需要变形一下,不只是单纯的对行列进行连边,首先对于一个空格,我们知道它最多影响2个线索:1个横向的和1个纵向的
    所以我们可以对于每个线索划分出一个块,这个块包含所有的能影响它的空格以及它自己,显然这个做法和行列连边是同性质的,正确性亦显然
    所以我们就得出了一个位置最多属于两个块,这个时候如果不考虑修改已经可以判断当前局面是否可行了
    但是需要考虑修改,发现一个位置修改后的权值范围是([1,+infty]),上下界预定了。
    对于第(i)行第(j)列的这个点来说,如果它不是既不是空格也不是线索。
    假设它纵向属于第x块,横向属于第y块(假如x为0就将x改为源点,y为0就将y改为汇点,自己思考,很显然的)
    以下的连边都是以这样的格式(add(x,y,down,up,cost))(x连y,下界,上界,费用)
    第一种情况:这是一个空格
    显然需要先建出(add(x,y,val,val,0))
    假如这个点可以修改,建出(add(x,y,0.inf,cost),add(y,x,0,val-1,cost))
    这两条边是分别代表增加权值和减少权值,看不懂自己思考,很显然的
    第二种情况:这个位置有左下角的线索(有右上角也没关系)
    先建出(add(s,x,val,val,0))
    假如这个点可以修改,建出(add(s,x,0.inf,cost),add(x,s,0,val-1,cost))
    边的含义同第一种情况
    第三种情况:这个位置有右上角的线索(有左下角也没关系)
    先建出(add(y,t,val,val,0))
    假如这个点可以修改,建出(add(y,t,0.inf,cost),add(t,y,0,val-1,cost))
    边的含义同前两种情况


    P.S:
    1、上下界网络流的细节我就不赘言了,来写的肯定都会吧
    2、本人阐述能力有点差,不懂只能看代码了
    3、本人代码费用流部分写的zkw费用流,其他费用流算法也是可以的
    代码:

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    void read(int &x) {
    	char ch; bool ok;
    	for(ok=0,ch=getchar(); !isdigit(ch); ch=getchar()) if(ch=='-') ok=1;
    	for(x=0; isdigit(ch); x=x*10+ch-'0',ch=getchar()); if(ok) x=-x;
    }
    #define ll long long
    #define rg register
    const int maxn=1e5+10;const ll inf=1e18;
    int s,t,ss,tt,n,cnt=1,m,id,in[maxn],mp[31][31],val[70][70][3],va[70][70],vb[70][70];
    int pre[maxn*2],nxt[maxn*2],h[maxn],w[maxn*2];
    int cost[70][70][3];ll sum,ans,flow,v[maxn*2],tot,dis[maxn],sla[maxn];
    bool vis[maxn];
    void add(int x,int y,ll z,int d)
    {
    	if(!z)return ;
    	pre[++cnt]=y,nxt[cnt]=h[x],h[x]=cnt,v[cnt]=z,w[cnt]=d;
    	pre[++cnt]=x,nxt[cnt]=h[y],h[y]=cnt,v[cnt]=0,w[cnt]=-d;
    }
    ll dfs(int x,ll flow)
    {
    	if(x==tt||!flow){ans+=dis[tt]*flow,tot+=flow;return flow;}
    	ll f=flow;vis[x]=1;
    	for(rg int i=h[x];i;i=nxt[i])
    		if(!vis[pre[i]]&&v[i])
    		{
    			if(dis[x]+w[i]==dis[pre[i]])
    			{
    				ll y=dfs(pre[i],min(f,v[i]));
    				f-=y,v[i]-=y,v[i^1]+=y;
    				if(!f)return flow;
    			}
    			else sla[pre[i]]=min(sla[pre[i]],dis[x]+w[i]-dis[pre[i]]);
    		}
    	return flow-f;
    }
    bool aug()
    {
    	ll mx=inf;
    	for(rg int i=s;i<=tt;i++)if(!vis[i])mx=min(mx,sla[i]),sla[i]=inf;
    	if(mx==inf)return 1;
    	for(rg int i=s;i<=tt;i++)if(!vis[i])dis[i]+=mx;
    	return 0;
    }
    int main()
    {
    	read(n),read(m);
    	for(rg int i=1;i<=n;i++)for(rg int j=1;j<=m;j++)read(mp[i][j]);
    	for(rg int i=1;i<=n;i++)
    		for(rg int j=1;j<=m;j++)
    		{
    			if(mp[i][j]==1)read(val[i][j][0]);
    			if(mp[i][j]==2)read(val[i][j][1]);
    			if(mp[i][j]==3)read(val[i][j][0]),read(val[i][j][1]);
    			if(mp[i][j]==4)read(val[i][j][2]);
    			if(mp[i][j]==1||mp[i][j]==3)
    			{
    				int now=++id,k=i;va[i][j]=now;k++;
    				while(mp[k][j]==4)va[k][j]=now,k++;
    			}
    			if(mp[i][j]==2||mp[i][j]==3)
    			{
    				int now=++id,k=j;vb[i][j]=now;k++;
    				while(mp[i][k]==4)vb[i][k]=now,k++;
    			}
    		}
    	s=0,t=++id;add(t,s,inf,0);ss=++id,tt=++id;
    	for(rg int i=1;i<=n;i++)
    		for(rg int j=1;j<=m;j++)
    		{
    			if(!mp[i][j])continue;
    			if(mp[i][j]==1)read(cost[i][j][0]);
    			if(mp[i][j]==2)read(cost[i][j][1]);
    			if(mp[i][j]==3)read(cost[i][j][0]),read(cost[i][j][1]);
    			if(mp[i][j]==4)read(cost[i][j][2]);
    			int x=va[i][j]?va[i][j]:s,y=vb[i][j]?vb[i][j]:t;
    			if(mp[i][j]==4)
    			{
    				add(x,y,0,0),in[x]-=val[i][j][2],in[y]+=val[i][j][2];
    				if(cost[i][j][2]!=-1)
    					add(x,y,inf,cost[i][j][2]),add(y,x,val[i][j][2]-1,cost[i][j][2]);
    			}
    			if(mp[i][j]==1||mp[i][j]==3)
    			{
    				add(s,x,0,0),in[s]-=val[i][j][0],in[x]+=val[i][j][0];
    				if(cost[i][j][0]!=-1)
    					add(s,x,inf,cost[i][j][0]),add(x,s,val[i][j][0]-1,cost[i][j][0]);
    			}
    			if(mp[i][j]==2||mp[i][j]==3)
    			{
    				add(y,t,0,0),in[y]-=val[i][j][1],in[t]+=val[i][j][1];
    				if(cost[i][j][1]!=-1)
    					add(y,t,inf,cost[i][j][1]),add(t,y,val[i][j][1]-1,cost[i][j][1]);
    			}
    		}
    	for(rg int i=s;i<=tt;i++)
    	{
    		if(in[i]>0)sum+=in[i],add(ss,i,in[i],0);
    		if(in[i]<0)add(i,tt,-in[i],0);
    	}
    	memset(sla,127,sizeof sla);
    	while(1)
    	{
    		while(1){memset(vis,0,sizeof vis);if(!dfs(ss,inf))break;}
    		if(aug())break;
    	}
    	printf("%lld
    ",tot==sum?ans:-1);
    }
    
  • 相关阅读:
    前端启动摄像头的API
    落谷训练---
    树的遍历 (和) 玩转二叉树 的总结博客
    L2-010 排座位 (并查集)
    最长回文(manacher模板)
    L2-006 树的遍历
    面试题5:从尾到头打印链表
    面试题4:替换空格
    面试题3:二维数组中的查找
    poj 1511(spfa)
  • 原文地址:https://www.cnblogs.com/lcxer/p/10451879.html
Copyright © 2011-2022 走看看