链接
https://www.luogu.org/problemnew/show/P2604
大意
- 给定一张图,求其的最大流
- 给定改动每条边的价值,在使最大流增加 的情况的最小价值
思路
不得不说,这是一个练习网络流的好题
对于第一问,直接跑一遍最大流
对于第二问,新增一群容量无穷大的没有代价的边(也就是相当于不会删掉它们),然后再新建一个点,将其连向汇点,代价为0,容量为题目所需的增加后的最大流,那么这个时候再跑一遍费用流,其会选择这条代价为0,容量为需要的流量的边,或者是代价更下的边,这样就确保了一定有一条最大流满足最小费用
代码
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<queue>
#define N 50001
#define reads(a,b,c,d) a=read();b=read();c=read();d=read()
using namespace std;int f,n,m,s,t,ans1,ans2,u[5001],v[5001],w[5001],g[5001],k;char c;
int read()
{
f=0;
while(c=getchar(),c<=47||c>=58);f=(f<<3)+(f<<1)+c-48;
while(c=getchar(),c>=48&&c<=57) f=(f<<3)+(f<<1)+c-48;
return f;
}
struct node{int next,to,w,g;}e[N<<1];
int dis[N],l[N],cf[N],tot,pos[N],d[N];
bool vis[N];
void add(int u,int v,int w,int c)//建边
{
e[tot]={l[u],v,w,c};l[u]=tot++;
e[tot]={l[v],u,0,-c};l[v]=tot++;
return;
}
bool bfs()//建造分层图
{
memset(d,-1,sizeof(d));
queue<int>q;d[s]=0;q.push(s);
while(q.size())
{
int x=q.front();q.pop();
for(int i=l[x];~i;i=e[i].next)
{
int y=e[i].to;
if(e[i].w&&d[y]==-1)
{
d[y]=d[x]+1;
q.push(y);
if(y==t) return true;
}
}
}
return false;
}
int dfs(int x,int flow)//求可行流
{
if(x==t||!flow) return flow;
int rest=0,k=0;
for(int i=l[x];~i;i=e[i].next)
{
int y=e[i].to;
if(d[x]+1==d[y]&&e[i].w)
{
f=dfs(y,min(flow-rest,e[i].w));
if(!f) d[y]=-1;
e[i].w-=f;rest+=f;e[i^1].w+=f;
}
}
if(!rest) d[x]=-1;
return rest;
}
int dinic()//求最大流
{
int r=0;
while(bfs()) r+=dfs(s,50234567);
return r;
}
bool spfa()//KE算法求费用流
{
fill(dis+1,dis+1+t,50234567);
memset(vis,0,sizeof(vis));
queue<int>q;q.push(s);dis[s]=0;vis[s]=true;cf[s]=50234567;
while(q.size())
{
int x=q.front();q.pop();vis[x]=true;
for(int i=l[x];~i;i=e[i].next)
{
int y=e[i].to,w=e[i].g;
if(e[i].w&&dis[y]>dis[x]+w)
{
dis[y]=dis[x]+w;
pos[y]=i;
cf[y]=min(cf[x],e[i].w);
if(!vis[y]) q.push(y),vis[y]=true;
}
}
vis[x]=false;
}
return dis[t]<50234567;
}
void updata()//更改每条边的容量
{
int x=t;
while(x!=s)
{
int i=pos[x];
e[i].w-=cf[t];
e[i^1].w+=cf[t];
x=e[i^1].to;
}
ans2+=dis[t]*cf[t];//累计答案
return;
}
void EK()//求费用流
{
while(spfa()) updata();return;
}
int main()
{
memset(l,-1,sizeof(l));
n=read();m=read();k=read();s=1;t=n;//输入
for(int i=1;i<=m;i++) {reads(u[i],v[i],w[i],g[i]);add(u[i],v[i],w[i],0);}//建边
ans1=dinic();//求最大流
memset(&e,0,sizeof(e));tot=0;memset(l,-1,sizeof(l));//废掉原图,重新建图
for(int i=1;i<=m;i++) add(u[i],v[i],1e9,g[i]),add(u[i],v[i],w[i],0);//建上两种边
add(n,n+1,k+ans1,0);t=n+1;//新增一个点,连接汇点
EK();//求费用流
printf("%d %d",ans1,ans2);//输出,endl
}