1. 程式人生 > 其它 >計蒜客 91 地鐵 & HDU 5263 平衡大師(二分+網路流)

計蒜客 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