被大佬扯去學有上下界的網絡流
拖了兩個星期之後大佬終於在我的不懈催促下開始找論文
學了半個星期了但是至今仍未找到題可做……
這是一篇好blog:https://www.cnblogs.com/liu-runda/p/6262832.html
(超級無敵詳細!就是在看的時候差一點不認識”流“字了……)
caioj1186: 無源匯有上下界可行流(模板)
http://caioj.cn/problem.php?id=1186
負責人的blog:https://blog.csdn.net/hanks_o/article/details/77970978
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
struct node
{
int x,y,c,next,other;
}a[41000];
int last[210],len;
int dep[210],list[210];
int d[21000],cs[21000],id[21000],ans[21000];//d:初始流中的關係 cs:初始流 id:原來對應的是哪條邊
int st,ed,n,m;
void build(int x,int y,int c,int now)
{
len++;int k1=len;id[len]=now;
a[len].x=x;a[len].y=y;a[len].c=c;a[len].next=last[x];last[x]=len;
len++;int k2=len;id[len]=0;
a[len].x=y;a[len].y=x;a[len].c=0;a[len].next=last[y];last[y]=len;
a[k1].other=k2;a[k2].other=k1;
}
bool bfs()
{
memset(dep,0,sizeof(dep));
dep[st]=1;list[1]=st;
int head=1,tail=1;
while (head<=tail)
{
int x=list[head];
for (int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if (dep[y]==0&&a[k].c>0)
{
dep[y]=dep[x]+1;
tail++;
list[tail]=y;
}
}
head++;
}
if (dep[ed]==0) return 0;
else return 1;
}
int dfs(int x,int flow)
{
if (x==ed) return flow;
int tot=0;
for (int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if (flow-tot>0&&a[k].c>0&&dep[y]==dep[x]+1)
{
int sum=dfs(y,min(flow-tot,a[k].c));
tot+=sum;a[k].c-=sum;a[a[k].other].c+=sum;
}
}
if (tot==0) dep[x]=0;
return tot;
}
int main()
{
//流量=初始流量+附加流量
//如果滿足條件 即初始流+附加流量後能夠守恆
//因爲初始流量不一定守恆 所以附加流量也不一定守恆
//即滿足:如果初始流量的流入量=流出量+x 附加流中流出量=流入量+x
// 初始流量的流入量=流出量-x 附加流中流出量=流入量-x
// 初始流量滿足流量守恆 附加流也滿足流量守恆
//因爲附加流是無源匯不滿足流量守恆 而dinic求出的是有源匯滿足流量守恆 所以我們需要新建一些邊來滿足流量守恆後再來跑dinic 從而求出不滿足流量守恆的情況
scanf("%d%d",&n,&m);
memset(last,0,sizeof(last));len=0;
memset(ans,0,sizeof(ans));
memset(d,0,sizeof(d));
memset(cs,0,sizeof(cs));
memset(id,0,sizeof(id));
for (int i=1;i<=m;i++)
{
int x,y,up,low;
scanf("%d%d%d%d",&x,&y,&low,&up);
//直接讓答案爲low 那麼這條邊可以接受的流量就是low-low~up-low 即0~up-low
build(x,y,up-low,i);
d[x]-=low;d[y]+=low;//有一條x~y流量爲low的邊 那麼原來在x的有low的流量流到了y x少了low的流量 y多了low的流量
cs[i]=low;
}
int klen=len;
st=n+1;ed=st+1;
for (int i=1;i<=n;i++)
{
//如果每個點流進來的流量能夠和流出去的流量相等 即d=0 一定滿足條件 如果不相等 就要把這些
if (d[i]>0)//如果到這個點的流量大於0 附加流量流進來的多於流出去的(初始流量中流出去的多於流進來的) 那麼要讓這些多的流出去的流量有一個來頭 讓源點給他一點流量使得他平衡
{
build(st,i,d[i],0);
}
if (d[i]<0) //如果到這個點的流量小於0 附加流量流出去的多於流進來的 (初始流量流進來的多餘於流出去的) 那麼要讓這些多的流進來流量有一個去處 讓匯點分掉一點流量
{
build(i,ed,-d[i],0);
}
}
int sum=0;
while (bfs()) sum+=dfs(st,999999999);
//如果流量守恆 指向st的邊的流量和(d[i]>0) 和指向ed的邊的流量和(d[i]<0) 應該是相等的 即 Σd[i] =0;
//如果st-i滿流 說明能夠滿足i-j這些點之間的要求
bool bk=true;
for (int k=last[st];k;k=a[k].next)
{
if (a[k].c>0) {bk=false;break;}
}
if (bk==false)
{
printf("NO\n");
}
else
{
printf("YES\n");
//流過去多少流量=原來的容量-現在的容量=反向邊的流量
for (int i=1;i<=klen;i+=2)
{
ans[id[i]]=a[a[i].other].c;
}
for (int i=1;i<=m;i++)
{
printf("%d\n",ans[i]+cs[i]);
}
}
return 0;
}
caioj1187: 有源匯有上下界最大流(模板)
http://caioj.cn/problem.php?id=1187
負責人的blog:https://blog.csdn.net/hanks_o/article/details/77984623
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
struct node
{
int x,y,c,next,other;
}a[41000];
int last[210],len;
int dep[210],list[210];
int d[21000],id[21000];
int st,ed;
void build(int x,int y,int c,int now)
{
len++;int k1=len;id[len]=now;
a[len].x=x;a[len].y=y;a[len].c=c;a[len].next=last[x];last[x]=len;
len++;int k2=len;id[len]=0;
a[len].x=y;a[len].y=x;a[len].c=0;a[len].next=last[y];last[y]=len;
a[k1].other=k2;a[k2].other=k1;
}
bool bfs()
{
memset(dep,0,sizeof(dep));
dep[st]=1;list[1]=st;
int head=1,tail=1;
while (head<=tail)
{
int x=list[head];
for (int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if (dep[y]==0&&a[k].c>0)
{
dep[y]=dep[x]+1;
list[++tail]=y;
}
}
head++;
}
if (dep[ed]==0) return 0;
return 1;
}
int dfs(int x,int flow)
{
if (x==ed) return flow;
int tot=0;
for (int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if (dep[y]==dep[x]+1&&a[k].c>0&&flow>tot)
{
int sum=dfs(y,min(flow-tot,a[k].c));
tot+=sum;a[k].c-=sum;a[a[k].other].c+=sum;
}
}
if (tot==0) dep[x]=0;
return tot;
}
int main()
{
int n,m,s,t;
scanf("%d%d%d%d",&n,&m,&s,&t);
for (int i=1;i<=m;i++)
{
int x,y,up,low;
scanf("%d%d%d%d",&x,&y,&low,&up);
build(x,y,up-low,i);
d[x]-=low;d[y]+=low;
}
st=n+1;ed=st+1;
int klen=len;
for (int i=1;i<=n;i++)
{
if (d[i]<0)
{
build(i,ed,-d[i],0);
}
if (d[i]>0)
{
build(st,i,d[i],0);
}
}
build(t,s,999999999,0);//匯點連到源點 相當於取消了源匯 直接按第一題做法來做
int sum=0;
while (bfs()) sum+=dfs(st,999999999);
bool bk=true;
for (int k=last[st];k;k=a[k].next)
{
if (a[k].c>0) {bk=false;break;}
}
if (bk==false) printf("please go home to sleep\n");
else
{
int ans=a[a[last[t]].other].c;//流到匯點的流量
for (int i=1;i<=len;i++) if (id[i]==0) a[i].c=0;//id爲0的不是反向邊就是新增的邊
last[st]=0;last[ed]=0;
st=s;ed=t;
while (bfs()) ans+=dfs(st,999999999);
printf("%d",ans);
}
return 0;
}
caioj1188: 有源匯有上下界最小流(模板)
http://caioj.cn/problem.php?id=1188
負責人的blog:https://blog.csdn.net/hanks_o/article/details/77995557
第一種方法:
先跑一次可行流 滿足下界
這時候只要在殘量網絡上求出最小流就好了
因爲反向邊的等價於正向邊的減小
那麼求出t到s的最大流就等效於s到t的最小流
波老師沒有寫第一種方法的博客 那我這裏補充一下好了
//hanks_o 思路1
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
struct node
{
int x,y,next,other;
long long c;
}a[510000];
int last[51000],len;
int list[51000],dep[51000];
long long d[51000];
int id[260000];
int st,ed;
long long inf=1e10;
void build(int x,int y,long long c,int now)
{
len++;int k1=len;id[len]=now;
a[len].x=x;a[len].y=y;a[len].c=c;a[len].next=last[x];last[x]=len;
len++;int k2=len;id[len]=0;
a[len].x=y;a[len].y=x;a[len].c=0;a[len].next=last[y];last[y]=len;
a[k1].other=k2;a[k2].other=k1;
}
bool bfs()
{
memset(dep,0,sizeof(dep));
dep[st]=1;list[1]=st;
int head=1,tail=1;
while (head<=tail)
{
int x=list[head];
for (int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if (a[k].c>0&&dep[y]==0)
{
dep[y]=dep[x]+1;
list[++tail]=y;
}
}
head++;
}
if (dep[ed]==0) return 0;
return 1;
}
long long dfs(int x,long long flow)
{
if (x==ed) return flow;
long long tot=0;
for (int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if (dep[y]==dep[x]+1&&a[k].c>0&&flow-tot>0)
{
long long sum=dfs(y,min(a[k].c,flow-tot));
a[k].c-=sum;a[a[k].other].c+=sum;tot+=sum;
}
}
if (tot==0) dep[x]=0;
return tot;
}
int main()
{
int n,m,s,t;
scanf("%d%d%d%d",&n,&m,&s,&t);
for (int i=1;i<=m;i++)
{
int x,y;
long long up,low;
scanf("%d%d%lld%lld",&x,&y,&low,&up);
build(x,y,up-low,i);
d[x]-=low;d[y]+=low;
}
st=n+1;ed=st+1;
int klen=len;
for (int i=1;i<=n;i++)
{
if (d[i]>0) build(st,i,d[i],0);
if (d[i]<0) build(i,ed,-d[i],0);
}
build(t,s,inf,0);
long long sum=0,ans;
while (bfs()) sum+=dfs(st,inf);
//while (bfs()) sum+=dfs(st,inf);
ans=a[len].c;
bool bk=true;
for (int k=last[st];k;k=a[k].next)
{
if (a[k].c>0) {bk=false;break;}
}
if (bk==false)
{
printf("please go home to sleep\n");
}
else
{
//把t到s的邊和所有st和ed的連邊都刪掉
for (int k=last[st];k;k=a[k].next) a[k].c=0;
for (int k=last[ed];k;k=a[k].next) a[k].c=0;
last[t]=a[last[t]].next;
last[s]=a[last[s]].next;
a[len].c=0;a[len-1].c=0; len-=2;
last[st]=0;last[ed]=0;
st=t;ed=s; sum=0;
while (bfs()) sum+=dfs(st,inf);
printf("%lld",ans-sum);
}
return 0;
}
方法二:
建立超級源和超級匯之後呢。
先跑一次最大流。
然後t到s連一條無窮大的邊。
再跑一次最大流。
最後的答案就是無窮大的邊(t到s那條)流過的流量。
有點難理解。
首先因爲整個圖滿足流量平衡。
所以說t點流入的流量等於流出的流量。
那麼他流出的流量只有那一條無窮大的邊而已。
所以說最小流就是求那條邊(無窮邊)流過的流量儘量小而已。
所以說第一次流最大流的時候已經滿足下界。那麼滿足下界能流的邊已經流滿。
所以殘量網絡剩下的最大流就會盡可能的小了。
//hanks_o 思路二
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
struct node
{
int x,y,next,other;
long long c;
}a[510000];
int last[51000],len;
int list[51000],dep[51000];
long long d[51000];
int id[260000];
int st,ed;
long long inf=1e10;
void build(int x,int y,long long c,int now)
{
len++;int k1=len;id[len]=now;
a[len].x=x;a[len].y=y;a[len].c=c;a[len].next=last[x];last[x]=len;
len++;int k2=len;id[len]=0;
a[len].x=y;a[len].y=x;a[len].c=0;a[len].next=last[y];last[y]=len;
a[k1].other=k2;a[k2].other=k1;
}
bool bfs()
{
memset(dep,0,sizeof(dep));
dep[st]=1;list[1]=st;
int head=1,tail=1;
while (head<=tail)
{
int x=list[head];
for (int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if (a[k].c>0&&dep[y]==0)
{
dep[y]=dep[x]+1;
list[++tail]=y;
}
}
head++;
}
if (dep[ed]==0) return 0;
return 1;
}
long long dfs(int x,long long flow)
{
if (x==ed) return flow;
long long tot=0;
for (int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if (dep[y]==dep[x]+1&&a[k].c>0&&flow-tot>0)
{
long long sum=dfs(y,min(a[k].c,flow-tot));
a[k].c-=sum;a[a[k].other].c+=sum;tot+=sum;
}
}
if (tot==0) dep[x]=0;
return tot;
}
int main()
{
int n,m,s,t;
scanf("%d%d%d%d",&n,&m,&s,&t);
for (int i=1;i<=m;i++)
{
int x,y;
long long up,low;
scanf("%d%d%lld%lld",&x,&y,&low,&up);
build(x,y,up-low,i);
d[x]-=low;d[y]+=low;
}
st=n+1;ed=st+1;
int klen=len;
for (int i=1;i<=n;i++)
{
if (d[i]>0) build(st,i,d[i],0);
if (d[i]<0) build(i,ed,-d[i],0);
}
long long sum=0,ans;
while (bfs()) sum+=dfs(st,inf);
build(t,s,inf,0);
while (bfs()) sum+=dfs(st,inf);
bool bk=true;
for (int k=last[st];k;k=a[k].next)
{
if (a[k].c>0) {bk=false;break;}
}
if (bk==false)
{
printf("please go home to sleep\n");
}
else
{
ans=a[len].c;
printf("%lld",ans);
}
return 0;
}