jzoj2701. 矩阵(上下界网络流)

7 篇文章 0 订阅
6 篇文章 0 订阅

题目描述

Description
在这里插入图片描述
Input
在这里插入图片描述
Output
在这里插入图片描述
Sample Input
2 2
0 1
2 1
0 1
Sample Output
1
1 1
1 1
Data Constraint
在这里插入图片描述

上下界网络流

就是像普通的网络流那样,只不过每条边都有一个下界,要求每条边的流都至少要达到这个下界
在这里插入图片描述
因为每条边都要达到下界,所以考虑把下界单独变成一条弧,剩下的就是流量为(上界-下界)的边
显然这样和原图是等价的
在这里插入图片描述
每个下界都要求满流,所以每条红色边都要满,考虑把所有红色边拉出来单独考虑
在这里插入图片描述
根据流量平衡,每个点的入=出,所以先把每个点分配等同于入度的流(满足下界),然后让其流到其它点,使每个点满足流量平衡,如果可以流满则可以满足流量平衡
新建一个超级源和超级汇,然后由超级源向每个点连等同于下界入度的边(流入),由每个点向超级汇连等同于下界出度的边(流出)
在这里插入图片描述
但是由于原图有源汇,也就是说可以向s流入一个流,再从t流出等价的流
显然t流出多少s就流入多少,也就相当于由t向s连了一条流量为+∞的边
在这里插入图片描述
最后以y为源点,x为汇点做最大流,判断连向x的边是否满流,如果满流则可以满足要求

100%

显然最终的答案为max(|a每行的和-b每行的和|,|a每列的和-b每列的和|),考虑二分这个答案ans
则每行就相当于有ans的容错,也就是说
a每行的和-ans≤b每行的和≤a每行的和+ans
列同理
所以由源点向每一行连向[该行的和-ans,该行的和+ans],由每一行向每一列连向[L,R],由每一列向汇点连向[该列的和-ans,该列的和+ans],然后判断是否有可行流
由源点向每一行和由每一列向汇点连边,就相当于规定了该行/列的总和范围,然后每个数都在[L,R]的范围内,就是行与列之间的连边

因为边比较多,所以可以把上/下界累加到一起最后一起连边

code

我以前写的sap和dinic都是假的
可以参考cold_chair大佬的网络流模板

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define max(a,b) (a>b?a:b)
#define min(a,b) (a<b?a:b)
using namespace std;

int A[201];
int B[201];
int a[400001][3];
int ls[501];
int cur[501];
int f[501];
int g[501];
int d[10001];
int in[501];
int out[501];
int n,m,i,j,k,l,r,mid,len,L,R,sum,begin,end;
int ans;

void New(int x,int y,int z)
{
	++len;
	a[len][0]=y;
	a[len][1]=z;
	a[len][2]=ls[x];
	ls[x]=len;
}

void NEW(int x,int y,int l,int r)
{
	New(x,y,r-l);
	New(y,x,0);
	in[y]+=l;
	out[x]+=l;
//	New(x,n+m+3,l);
//	New(n+m+3,x,0);
//	New(n+m+2,y,l);
//	New(y,n+m+2,0);
	
	sum+=l;
}

void bfs()
{
	int h=0,t=1,i;
	
	f[end]=0;
	d[1]=end;
	while (h<t)
	{
		for (i=ls[d[++h]]; i; i=a[i][2])
		if (a[i^1][1] && f[a[i][0]]>f[d[h]]+1)
		{
			f[a[i][0]]=f[d[h]]+1;
			d[++t]=a[i][0];
		}
	}
	fo(i,0,end) if (f[i]>2333) f[i]=n+m+4;
	fo(i,0,end) ++g[f[i]];
}

int dfs(int t,int flow)
{
	int F=0,i,Flow;
	
	if (t==end) return flow;
	
	for (i=cur[t]; i; i=a[i][2])
	if (a[i][1] && f[t]==f[a[i][0]]+1)
	{
		Flow=dfs(a[i][0],min(flow-F,a[i][1]));
		
		F+=Flow;
		a[i][1]-=Flow;
		a[i^1][1]+=Flow;
		cur[t]=i;
		
		if (F==flow) return flow;
	}
	
	cur[t]=ls[t];
	if (!(--g[f[t]++]))
	{
		f[begin]=n+m+4;
		return F;
	}
	++g[f[t]];
	return F;
}

bool pd(int t)
{
	int i,j;
	
	memset(ls,0,sizeof(ls));
	memset(f,1,sizeof(f));
	memset(g,0,sizeof(g));
	memset(in,0,sizeof(in));
	memset(out,0,sizeof(out));
	len=1;
	sum=0;
	
	fo(i,1,n)
	{
		fo(j,1,m)
		NEW(i,n+j,L,R);
	}
	fo(i,1,n)
	NEW(0,i,max(A[i]-t,0),A[i]+t);
	fo(i,1,m)
	NEW(n+i,n+m+1,max(B[i]-t,0),B[i]+t);
	NEW(n+m+1,0,0,233333333);
	
	fo(i,0,end)
	{
		New(begin,i,in[i]);
		New(i,begin,0);
		New(i,end,out[i]);
		New(end,i,0);
		cur[i]=ls[i];
	}
	g[0]=n+m+4;
	bfs();
	
	ans=0;
	while (f[begin]<=n+m+3)
	ans+=dfs(begin,233333333);
	
	return ans==sum;
}

int main()
{
	scanf("%d%d",&n,&m);len=1;
	begin=n+m+2;
	end=n+m+3;
	
	fo(i,1,n)
	{
		fo(j,1,m)
		{
			scanf("%d",&k);
			A[i]+=k;
			B[j]+=k;
		}
	}
	scanf("%d%d",&L,&R);
	
	l=0;
	r=200000;
	while (l<r)
	{
		mid=(l+r)/2;
		
		if (!pd(mid))
		l=mid+1;
		else
		r=mid;
	}
	printf("%d\n",l);
	
	pd(l);
	k=3;
	fo(i,1,n)
	{
		fo(j,1,m)
		printf("%d ",a[k][1]+L),k+=2;
		printf("\n");
	}
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值