省选模拟 集训试题0403 矩阵【二分答案+上下界网络流】

题面:

在这里插入图片描述
在这里插入图片描述
二分答案,那么每一行每一列的B矩阵的和就有一个范围 [ s u m − m i d , s u m + m i d ] [sum-mid,sum+mid] [summid,sum+mid]
s u m sum sum为这一行或这一列A矩阵的和。
可以看出这是符合二分性质的,只需要判断是否可行。
B矩阵的每个点是[L,R],行和列和有上下界限制,上下界网络流模板
在这里插入图片描述
Code:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 415
#define maxm 200005
using namespace std;
const int inf = 0x3f3f3f3f;
int n,m,S,T,ss,tt,L,R,tf[maxn],sr[maxn],sc[maxn],a[maxn][maxn];
int fir[maxn],cur[maxn],nxt[maxm],to[maxm],cap[maxm],tot=1;
inline void line(int x,int y,int z){
	nxt[++tot]=fir[x],fir[x]=tot,to[tot]=y,cap[tot]=z;
	nxt[++tot]=fir[y],fir[y]=tot,to[tot]=x,cap[tot]=0;
}
namespace Maxflow{
	int d[maxn],vd[maxn],sz;
	int aug(int u,int augco){
		if(u==T) return augco;
		int need=augco,delta;
		for(int &i=cur[u];i;i=nxt[i]) if(cap[i]&&d[u]==d[to[i]]+1){
			delta=aug(to[i],min(cap[i],need));
			cap[i]-=delta,cap[i^1]+=delta;
			if(!(need-=delta)||d[S]==sz) return augco-need; 
		}
		cur[u]=fir[u];
		if(!(--vd[d[u]])) d[S]=sz;
		vd[++d[u]]++;
		return augco-need;
	}
	int SAP(){
		memset(d,0,(T+1)<<2);
		memset(vd,0,(T+1)<<2);
		int flow=0;sz=T+1;
		while(d[S]<sz) flow+=aug(S,inf);
		return flow;
	}
}
void add(int x,int y,int l,int r){tf[y]+=l,tf[x]-=l,line(x,y,r-l);}
bool check(int mid){
	memset(fir,0,sizeof fir),tot=1;
	memset(tf,0,sizeof tf);
	S=0,T=n+m+1,ss=T+1,tt=T+2;
	for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) add(i,n+j,L,R);
	for(int i=1;i<=n;i++) add(S,i,max(sr[i]-mid,0),sr[i]+mid);
	for(int i=1;i<=m;i++) add(n+i,T,max(sc[i]-mid,0),sc[i]+mid);
	int flow=0;
	for(int i=S;i<=T;i++)
		if(tf[i]<0) line(i,tt,-tf[i]);
		else if(tf[i]>0) line(ss,i,tf[i]),flow+=tf[i];
	line(T,S,inf);
	S=ss,T=tt;
	return Maxflow::SAP()==flow;
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%d",&a[i][j]),sr[i]+=a[i][j],sc[j]+=a[i][j];
	scanf("%d%d",&L,&R);
	int l=0,r=max(*max_element(sr+1,sr+1+n),*max_element(sc+1,sc+1+m)),mid;
	while(l<r){
		mid=(l+r)>>1;
		if(check(mid)) r=mid;
		else l=mid+1;
	}
	check(l); printf("%d\n",l);
	for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) printf("%d%c",cap[(((i-1)*m+j)<<1)^1]+L,j==m?10:32);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值