计蒜客 91 地铁 & HDU 5263 平衡大师(二分+网络流)

先说PPT的思路

PPT的思路源于这句话:
对每条边 (u, v),连一条 (u, v) 容量为 1,费用为 1 的边。如果
流了表示删去这条边。

流过原图上的边表示删去这条边意味着什么呢?
令dif[u]=u的出度-入度
在这里插入图片描述
如图,灰边表示原图上的边,初始状态没有流过任何边
因为原图没有边被删,所以dif[u]=-1

这时,如果有一流量为1的流流过边a,那么此流只能从u在原图上的出边流出,即有一流量为1的流流过了边2,代表原图中边2被删,dif[u]=dif[u]-1=-2

因此,s到u流一流量为Δx的流,dif[u]要-Δx

在这里插入图片描述
同理,如果有一流量为1的流流过边b,那么此流只能从u在原图上的入边流入,即有一流量为1的流流过了边1或边3,代表原图中边1或边3被删,dif[u]=dif[u]+1=0

因此,u到t流一流量为Δx的流,dif[u]要+Δx

这样,我们就将dif[u]值的变化量,转化成了流过(s,u)边或(u,t)边的流量

如此,限制入度与出度的差的绝对值的最大值就变得简单了
因此考虑二分答案

设v=入度与出度的差的绝对值的最大值,二分v,然后求最少需要删去多少的边,判断是否可行即可
在建图时,

1.若dif[u]>=v:
	则dif[u]要减去一个数x(x>=0,
	因为-v<=dif[u]-x<=v,所以dif[u]-v<=x<=dif[u]+v,
	从s到u连一条上界为dif[u]+v,下界为dif[u]-v,费用为0的边
2.-v<dif[u]<v:
	从s到u连一条上界为dif[u]+v,费用为0的边
	从u到t连一条上界为v-dif[u],费用为0的边
3.若dif[u]<=-v:
	从u到t连一条上界为v-dif[u],下界为-v-dif[u],费用为0的边

跑有源汇有上下界的最小费用最大流即可

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int N=60;
const int M=100005;
const int inf=0x7fffffff;
struct Edge{
	int u,v,f,w,nxt;
}edge[M<<1];
int s,t,ss,tt,S,T,head[N],cnt,inque[N],pre[N],dis[N];
queue<int> que;
int Q,n,m,K,a[M],b[M],dif[N];
void add(int u,int v,int f,int w){
	edge[cnt].u=u;edge[cnt].v=v;edge[cnt].w=w;edge[cnt].f=f;edge[cnt].nxt=head[u];head[u]=cnt++;
	edge[cnt].u=v;edge[cnt].v=u;edge[cnt].w=-w;edge[cnt].f=0;edge[cnt].nxt=head[v];head[v]=cnt++;
}
bool spfa(){
	memset(dis,0x7f,sizeof(dis));
	memset(inque,0,sizeof(inque));
	memset(pre,-1,sizeof(pre));
	dis[S]=0;
	que.push(S);inque[S]=1;
	while(!que.empty()){
		int u=que.front();
		que.pop();inque[u]=0;
		for(int i=head[u];i!=-1;i=edge[i].nxt){
			int v=edge[i].v;
			if(edge[i].f>0&&dis[v]>dis[u]+edge[i].w){
				dis[v]=dis[u]+edge[i].w;
				pre[v]=i;
				if(!inque[v]){
					que.push(v);inque[v]=1;
				}
			}
		}	
	}
	if(pre[T]==-1) return 0;
	return 1;
}
int EK(){
	int flow,ret=0;
	while(spfa()){
		flow=inf;
		int x=pre[T];
		while(x!=-1){
			flow=min(edge[x].f,flow);
			x=pre[edge[x].u];
		}
		x=pre[T];
		while(x!=-1){
			edge[x].f-=flow;
			edge[x^1].f+=flow;
			ret+=flow*edge[x].w;
			x=pre[edge[x].u];
		}
	}
	return ret;
}
bool check(int v){
	cnt=0;memset(head,-1,sizeof(head));
	s=n+1;t=n+2;ss=n+3;tt=n+4;
	add(t,s,inf,0);
	for(int i=1;i<=m;i++)
		add(a[i],b[i],1,1);
	for(int i=1;i<=n;i++){
		if(dif[i]>=v){
			add(s,i,2*v,0);
			add(ss,i,dif[i]-v,0);
			add(s,tt,dif[i]-v,0);
			//add(s,i,[dif[i]+v,dif[i]-v],0);//减法上下界 
		}
		else if(dif[i]>-v){
			add(s,i,dif[i]+v,0);//减法上界 
			add(i,t,v-dif[i],0);//加法上界 
		}
		else{
			add(i,t,2*v,0);
			add(ss,t,-v-dif[i],0);
			add(i,tt,-v-dif[i],0);
			//add(i,t,[v-dif[i],-v-dif[i]],0);//加法上下界 
		}
	}
	S=ss,T=tt;
	if(EK()<=K) return 1;
	return 0;
}
int main(){
	scanf("%d",&Q);
	for(int cas=1;cas<=Q;cas++){
		memset(dif,0,sizeof(dif));
		scanf("%d%d%d",&n,&m,&K);K=m-K;
		for(int i=1;i<=m;i++){
			scanf("%d%d",&a[i],&b[i]);
			dif[b[i]]--;dif[a[i]]++;
		}
		int l=0,r=n,mid,ans;
		while(l<=r){
			mid=(l+r)/2;
			if(check(mid)){
				ans=mid;r=mid-1;
			}
			else l=mid+1;
		} 
		printf("Case %d: %d\n",cas,ans);
	}
	return 0;
}

接下来是我自己的思考产物,还未验证

考虑如何转化 入度与出度之差
设原图每条边流量为1,入度与出度之差转化为一个点的流入量与流出量之差
此时图是不满足流量平衡的,所以对每个点,我们给少的流一个来处,多的流一个去处,
入度与出度之差就转化为从来处到节点的流(或从节点到去处的流的大小

顺着想下去,原图上的边有流流过,就代表选择了这条边,没有流流过,就代表边被删除

考虑如何求解

原题:最多k条边限制入度与出度之差的绝对值的最大值最小求最值
考虑二分答案入度与出度之差的绝对值变为限制(用限制流量实现),那么删去边数自然转为求的最值(用费用求),与d比较判断即可

update:
我的思路会产生一个问题,从源点到各节点的流的大小 代表 出度-入度, 从各节点到汇点的流的大小 代表 入度-出度,根据每个节点是出度大还是入度大,我们选择连(s,u)还是(u,t)(显然一个节点不能同时连两种边),但因为题目限制的是 出度与入度之差的绝对值,无论连哪种边,其下界都会是负数,目前我想到的唯一解决方法是将所有边的边界整体加上n

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值