【ZJOI2010】网络扩容
最大流+费用流
第一问就直接跑最大流。
第二问目前有两种方法:
- 保留残留网络,设费用为 0 0 0,然后复制原网络,但是容量为 i n f inf inf,费用即扩容用。 然后建立一个起点,向1连一条容量为 K K K,费用为 0 0 0的边。 然后在新图上跑最小费用最大流即可。(可以保证新流量为 K K K)
- 将参与网络的正边容量加上 K K K,加入扩容费用,跑费用流。(最初的想法,但是错误的,因为通往终点的不止有一条路,所以增加的流量应是 n ∗ K n*K n∗K)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
#define INF 0x7fffffff
int n,m,k,s,t;
struct edge{
int u,v,c,w,nxt;
}e[100005];
int first[50005],cur[50005],cnt=1;
inline void add(int u,int v,int c,int w){
e[++cnt].v=v;e[cnt].c=c;e[cnt].w=w;e[cnt].u=u;
e[cnt].nxt=first[u];first[u]=cnt;
}
//最大流
int dep[50005];
int bfs(){
queue<int>q;
q.push(s);
memset(dep,0,sizeof(dep));
dep[s]=1;
while(!q.empty()){
int x=q.front();q.pop();
for(int i=first[x];i;i=e[i].nxt){
int y=e[i].v;
if(e[i].c<1||dep[y])continue;//
dep[y]=dep[x]+1;
q.push(y);
if(y==t)return 1;
}
}
return 0;
}
int dfs(int x,int f){
if(x==t||f==0)return f;
int used=0;
for(int &i=cur[x];i;i=e[i].nxt){
int y=e[i].v;
if(e[i].c&&dep[y]==dep[x]+1){
int r=dfs(y,min(e[i].c,f));
if(!r)continue;
used+=r;f-=r;
e[i].c-=r;e[i^1].c+=r;
if(!f)break;
}
}
if(!used)dep[x]=-1;
return used;
}
int dinic(){
int flow=0;
while(bfs()){//cout<<1;
memcpy(cur,first,sizeof(first));
flow+=dfs(s,INF);
}
return flow;
}
//费用流
int money=0,vis[50005],dis[50005];
int spfa(){//反向
memset(vis,0,sizeof(vis));//是否在队列里
memset(dis,0x3f,sizeof(dis));//最短路
dis[t]=0;vis[t]=1;
deque<int>q;//双端队列
q.push_back(t);
while(!q.empty()){
int u=q.front();q.pop_front();vis[u]=0;
for(int i=first[u];i;i=e[i].nxt){
if(e[i^1].c&&dis[u]-e[i].w<dis[e[i].v]){//反向的反向 反向的是负边
dis[e[i].v]=dis[u]-e[i].w;//
if(!vis[e[i].v]){
vis[e[i].v]=1;//~~~
if(!q.empty()&&dis[e[i].v]<dis[q.front()])
q.push_front(e[i].v);
else q.push_back(e[i].v);//如果最短路比队首小,就放队首让它先松弛其他点
}
}
}
}
//for(int i=1;i<=n;i++)cout<<dis[i]<<" ";
//cout<<endl;
return dis[s]<0x3f3f3f3f;//真1假0
}
int dfs2(int x,int f){
if(x==t){//f 费用
vis[t]=1;return f;
}
int used=0,k;vis[x]=1;
for(int i=first[x];i;i=e[i].nxt){
int v=e[i].v,c=e[i].c,ff=e[i].w;
if(!vis[v]&&c&&dis[x]-ff==dis[v]){//满足最短路
k=dfs2(v,min(c,f-used));
if(k){
money+=k*ff;used+=k;
e[i].c-=k;e[i^1].c+=k;
}//
if(used==f)break;
}
}
return used;
}
int ZKW(){
int flow=0;
while(spfa()){
vis[t]=1;
while(vis[t]){
memset(vis,0,sizeof(vis));
flow+=dfs2(s,INF);
}
}
return flow;
}
int h[100005];
int main(){
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=m;i++){
int u,v,c;
scanf("%d%d%d%d",&u,&v,&c,&h[i*2]);
h[i*2+1]=-h[i*2];
add(u,v,c,0);add(v,u,0,0);
}
s=1;t=n;
printf("%d ",dinic());
int g=cnt;
for(int i=2;i<=g;i++)
if(i%2==0)add(e[i].u,e[i].v,INF,h[i]);
else add(e[i].u,e[i].v,0,h[i]);
s=++n;
add(s,1,k,0);add(1,s,0,0);
ZKW();
printf("%d",money);
return 0;
}