BZOJ2406矩阵——有上下界的可行流+二分答案

题目描述

BZOJ2406矩阵——有上下界的可行流+二分答案

输入

第一行两个数n、m,表示矩阵的大小。

接下来n行,每行m列,描述矩阵A。

最后一行两个数L,R。

输出

第一行,输出最小的答案;

样例输入

2 2
0 1
2 1
0 1

样例输出

1

提示

对于100%的数据满足N,M<=200,0<=L<=R<=1000,0<=Aij<=1000

 

要求最大值最小,显然二分答案。

每次二分一个$mid$,设每行或每列的$A$之和为$VA$,$B$之和为$VB$,那么就要求每行或每列的$VA-mid\le VB\le VA+mid$,判断是否有可行解。

将源点与每行连边,流量上下界为$[VA-mid,VA+mid]$;每列与汇点连边,流量上下界为$[VA-mid,VA+mid]$;每行与每列连边,流量上下界为$[L,R]$。

每次二分跑一遍有源汇的上下界可行流判断是否满流即可。

注意$VA-mid$要与$0$取$max$。

如果输出方案的话,$b_{ij}$就是$L$加上第$i$行的点向第$j$列的点连的边的反向边流量。

#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#include<cstdio>
#include<bitset>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
int head[500];
int low[500];
int to[200010];
int val[200010];
int next[200010];
int tot=1;
int n,m;
int L,R;
int a[210][210];
int b[210][210];
int q[500];
int S,T;
int SS,TT;
int d[500];
int fx[210];
int fy[210];
int ans;
void add(int x,int y,int z)
{
    tot++;
    next[tot]=head[x];
    head[x]=tot;
    to[tot]=y;
    val[tot]=z;
    tot++;
    next[tot]=head[y];
    head[y]=tot;
    to[tot]=x;
    val[tot]=0;
}
bool bfs(int S,int T)
{
    memset(d,-1,sizeof(d));
    int l=0;
    int r=0;
    d[S]=0;
    q[r++]=S;
    while(l<r)
    {
        int now=q[l];
        l++;
        for(int i=head[now];i;i=next[i])
        {
            if(d[to[i]]==-1&&val[i])
            {
                d[to[i]]=d[now]+1;
                q[r++]=to[i];
            }
        }
    }
    return d[T]!=-1;
}
int dfs(int x,int maxflow)
{
    if(x==TT)
    {
        return maxflow;
    }
    int used=0;
    int nowflow;
    for(int i=head[x];i;i=next[i])
    {
        if(d[to[i]]==d[x]+1&&val[i])
        {
            nowflow=dfs(to[i],min(val[i],maxflow-used));
            val[i]-=nowflow;
            val[i^1]+=nowflow;
            used+=nowflow;
            if(nowflow==maxflow)
            {
                return maxflow;
            }
        }
    }
    if(!used)
    {
        d[x]=-1;
    }
    return used;
}
int dinic()
{
	int res=0;
    while(bfs(SS,TT))
    {
        res+=dfs(SS,0x3f3f3f3f);
    }
    return res;
}
bool check(int lim)
{
	int res=0;
	tot=1;
	memset(head,0,sizeof(head));
	memset(low,0,sizeof(low));
	add(T,S,1<<30);
	int sx,sy;
	for(int i=1;i<=n;i++)
	{
		sx=max(fx[i]-lim,0);
		sy=fx[i]+lim;
		add(S,i,sy-sx);
		low[S]-=sx,low[i]+=sx;
	}
	for(int i=1;i<=m;i++)
	{
		sx=max(fy[i]-lim,0);
		sy=fy[i]+lim;
		add(n+i,T,sy-sx);
		low[n+i]-=sx,low[T]+=sx;
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			add(i,n+j,R-L);
			low[i]-=L,low[n+j]+=L;
		}
	}
	for(int i=1;i<=T;i++)
	{
		if(low[i]>0)
		{
			add(SS,i,low[i]);
			res+=low[i];
		}
		else
		{
			add(i,TT,-low[i]);
		}
	}
	return res==dinic();
}
int main()
{
	scanf("%d%d",&n,&m);
	S=n+m+1,T=S+1,SS=T+1,TT=SS+1;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			scanf("%d",&a[i][j]);
			fx[i]+=a[i][j],fy[j]+=a[i][j];
		}
	}
	scanf("%d%d",&L,&R);
	int l=0,r=200000;
	while(l<=r)
	{
		int mid=(l+r)>>1;
		if(check(mid))
		{
			ans=mid,r=mid-1;
		}
		else
		{
			l=mid+1;
		}
	}
	printf("%d\n",ans);
	// check(ans);
	// for(int i=1;i<=n;i++)
	// {
	// 	for(int j=head[i];j;j=next[j])
	// 	{
	// 		if(to[j]>n&&to[j]<=n+m)
	// 		{
	// 			b[i][to[j]-n]=L+val[j^1];
	// 		}
	// 	}
	// }
	// for(int i=1;i<=n;i++)
	// {
	// 	for(int j=1;j<=m;j++)
	// 	{
	// 		printf("%d",b[i][j]);
	// 		if(j!=m)
	// 		{
	// 			printf(" ");
	// 		}
	// 	}
	// 	printf("\n");
	// }
}
上一篇:c语言的可变参数实例


下一篇:bzoj 2406: 矩阵【二分+有源汇上下界可行流】