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