所谓最小费用最大流,就是给边再加一个单位流量的费用,
每流过去单位流量, 就会花费这么多的费用
我们要求的就是在最大流下的最小费用
Dinic+SPFA
#include<cstdio> #include<iostream> #include<cstring> #include<cctype> #include<algorithm> #include<queue> using namespace std; #define int long long #define olinr return #define _ 0 #define love_nmr 0 #define DB double #define inf 0x7ff inline int read() { int x=0,f=1; char ch=getchar(); while(!isdigit(ch)) { if(ch=='-') f=-f; ch=getchar(); } while(isdigit(ch)) { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } inline void put(int x) { if(x<0) { x=-x; putchar('-'); } if(x>9) put(x/10); putchar(x%10+'0'); } struct DIJ { int id; int dis; friend bool operator < (const DIJ &a,const DIJ &b) { return a.dis>b.dis; } }; int s; int t; int n; int m; queue<int>q; struct node { int to; int dis; int nxt; int flow; int cap; }edge[105050]; int head[10505]; int cnt=-1; bool vis[10505]; int change[10505]; int road[10505]; int cost[10505]; int ans; int tot; int minn=0x7fffffff; inline void add(int from,int to,int dis,int cap,int flow) { cnt++; edge[cnt].cap=cap; edge[cnt].to=to; edge[cnt].nxt=head[from]; edge[cnt].dis=dis; edge[cnt].flow=flow; head[from]=cnt; } inline bool dij() { memset(change,0x7f,sizeof change); memset(cost,0x7f,sizeof cost); cost[s]=0; q.push(s); vis[s]=true; while(!q.empty()) { int tp=q.front(); q.pop(); vis[tp]=false; for(int i=head[tp];i!=-1;i=edge[i].nxt) { int go=edge[i].to; if(cost[go]>cost[tp]+edge[i].dis&&edge[i].cap>edge[i].flow) //spfa { cost[go]=cost[tp]+edge[i].dis; //更新花费 road[go]=i; //记录路径 change[go]=min(change[tp],edge[i].cap-edge[i].flow); //残量取min if(!vis[go]) { vis[go]=true; q.push(go); } } } } if(change[t]>1e9) return false; return true; } inline void Dinic() { while(dij()) { ans+=change[t]; //流 tot+=change[t]*cost[t]; //更新花费,流量*单价 for(int i=t;i!=s;i=edge[road[i]^1].to) { edge[road[i]].flow+=change[t]; //更新 edge[road[i]^1].flow-=change[t]; } } } signed main() { n=read(); m=read(); s=read(); t=read(); memset(head,-1,sizeof head); for(int a,b,c,d,i=1;i<=m;i++) { a=read(); b=read(); c=read(); d=read(); minn=min(minn,c); add(a,b,d,c,0); add(b,a,-d,0,0); } Dinic(); put(ans); putchar(' '); put(tot); olinr ~~(0^_^0)+love_nmr; }
不过。。。。。众所周知,SPFA他死了。。。。。。。
那么就得用dij了
然而。。。。。TM他不能处理负权啊,那咋办?
考虑加一个大数,最后再减回来?(可能炸long long QAQ)
我们给每个点定义一个势h
将转移从$dis_u=dis_v+w$ 改成$dis_u=dis_v+w+h_v-h_u$
并保证$w+h_v-h_u ge 0$
那么假如我们有一条$p_1---p_2---p_3---......p_n$的路径
那么路径长度为$w_1+w_2+w_3+.......+w_n+(h_2-h_1)+(h_3-h_2)+......+(h_n-h_{n-1})=$
后面的h就只剩起点与终点的h了,那么问题就解决了
现在要考虑的是,h怎么定义
我们把刚刚保证的式子变一下形
$w+h_vge h_u$
额 貌似。。。$w+dis_vge dis_u$
这在最短路是对于所有边恒成立的
所以我们把上一次的dis作为$h_i$每次让$h_i+=dis[i]$
#include<cstdio> #include<iostream> #include<cstring> #include<cctype> #include<algorithm> #include<queue> using namespace std; #define int long long #define olinr return #define _ 0 #define love_nmr 0 #define DB double #define inf 0x7ff inline int read() { int x=0,f=1; char ch=getchar(); while(!isdigit(ch)) { if(ch=='-') f=-f; ch=getchar(); } while(isdigit(ch)) { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } inline void put(int x) { if(x<0) { x=-x; putchar('-'); } if(x>9) put(x/10); putchar(x%10+'0'); } struct DIJ { int id; int dis; friend bool operator < (const DIJ &a,const DIJ &b) { return a.dis>b.dis; } }; int s; int t; int n; int m; priority_queue<DIJ> q; struct node { int to; int dis; int nxt; int flow; int cap; }edge[105050]; int head[10505]; int cnt=-1; bool vis[10505]; int change[10505]; int road[10505]; int cost[10505]; int h[10505]; int ans; int tot; int minn=0x7fffffff; inline void add(int from,int to,int dis,int cap,int flow) { cnt++; edge[cnt].cap=cap; edge[cnt].to=to; edge[cnt].nxt=head[from]; edge[cnt].dis=dis; edge[cnt].flow=flow; head[from]=cnt; } inline bool dij() { memset(vis,false,sizeof vis); memset(change,0x7f,sizeof change); memset(cost,0x7f,sizeof cost); cost[s]=0; q.push((DIJ){s,0}); while(!q.empty()) { DIJ tp=q.top(); q.pop(); if(vis[tp.id]) continue; vis[tp.id]=true; for(int i=head[tp.id];i!=-1;i=edge[i].nxt) { int go=edge[i].to; if(cost[go]>cost[tp.id]+edge[i].dis+h[tp.id]-h[go]&&edge[i].cap>edge[i].flow) //判断的时候带上势 { cost[go]=cost[tp.id]+edge[i].dis+h[tp.id]-h[go]; //带着势 road[go]=i; change[go]=min(change[tp.id],edge[i].cap-edge[i].flow); q.push((DIJ){go,cost[go]}); } } } if(change[t]>1e9) return false; return true; } inline void Dinic() { while(dij()) { ans+=change[t]; tot+=change[t]*(cost[t]-h[s]+h[t]); //只有起点终点影响tot for(int i=t;i!=s;i=edge[road[i]^1].to) { edge[road[i]].flow+=change[t]; edge[road[i]^1].flow-=change[t]; } for(int i=1;i<=n;i++) h[i]+=cost[i]; //别忘更新h } } signed main() { n=read(); m=read(); s=read(); t=read(); memset(head,-1,sizeof head); for(int a,b,c,d,i=1;i<=m;i++) { a=read(); b=read(); c=read(); d=read(); minn=min(minn,c); add(a,b,d,c,0); add(b,a,-d,0,0); } Dinic(); put(ans); putchar(' '); put(tot); olinr ~~(0^_^0)+love_nmr; }