使用势能函数解决问题。
因为边权为负时无法使用 dijkstra 求最短路,所以我们要使最短路不被改变的情况下使边权变为正。
不能直接加边,因为路径的边数不同。
可以证明当 h[i]=dis[i] 时 dis’[t]=dis[t]-h[t]。
当有反向边加入时依然成立。
那么先用一次 SPFA 求出原始 dis 即可。
code:
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
#define int long long
#define db double
const int MAXN=5e3+5;
const int MAXM=2e5+5;
const int INF=0x3f3f3f3f;
int n,m,ca[MAXM],cnt=1,head[MAXN],dis[MAXN],pre[MAXN],mf,mc,inc[MAXN],st=0,en,h[MAXN],s,t;
db Ans;
bool vis[MAXN];
struct EDG{
int nxt,to,z;
}a[MAXM];
struct ren{
int v,val;
ren(){};
ren(int V,int Val){
v=V;val=Val;
}
bool operator<(const ren &a)const{
return val>a.val;
}
};
priority_queue<ren> p;
void add(int x,int y,int c,int w)
{
a[++cnt].to=y;
a[cnt].nxt=head[x];
a[cnt].z=w;
head[x]=cnt;
ca[cnt]=c;
}
void spfa(int s,int t)
{
memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof(vis));
inc[s]=INF;
vis[s]=1;dis[s]=0;
queue<int> q;
q.push(s);
while(q.size()){
int now=q.front();
q.pop();
vis[now]=0;
for(int i=head[now];i;i=a[i].nxt){
if(!ca[i]) continue;
int v=a[i].to;
if(dis[v]>dis[now]+a[i].z)
{
dis[v]=dis[now]+a[i].z;
pre[v]=i;
inc[v]=min(inc[now],ca[i]);
if(!vis[v]) vis[v]=1,q.push(v);
}
}
}
}
bool dij(int s,int t)
{
memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof(vis));
inc[s]=INF;
dis[s]=0;
p.push((ren){s,0});
while(p.size()){
ren now=p.top();
p.pop();
if(vis[now.v]) continue;
vis[now.v]=1;
for(int i=head[now.v];i;i=a[i].nxt){
int v=a[i].to;
if(!ca[i]) continue;
if(dis[v]>dis[now.v]+a[i].z+h[now.v]-h[v]){
dis[v]=dis[now.v]+a[i].z+h[now.v]-h[v];
pre[v]=i;
inc[v]=min(inc[now.v],ca[i]);
p.push((ren){v,dis[v]});
}
}
}
return dis[t]<INF;
}
void MCMF(int s,int t){
mc=0;mf=0;
while(dij(s,t)){
int x=inc[t];
mf+=x;
mc+=(dis[t]+h[t])*x;
int op=t,i;
while(op!=s){
i=pre[op];
ca[i]-=x;
ca[i^1]+=x;
op=a[i^1].to;
}
for(int i=1;i<=n;i++){
h[i]+=dis[i];
}
}
}
signed main(){
scanf("%lld%lld%lld%lld",&n,&m,&s,&t);
for(int i=1;i<=m;i++){
int x,y,c,z;
scanf("%lld%lld%lld%lld",&x,&y,&c,&z);
add(x,y,c,z);add(y,x,0,-z);
}
spfa(s,t);
for(int i=1;i<=n;i++){
h[i]=dis[i];
}
MCMF(s,t);
printf("%lld %lld\n",mf,mc);
return 0;
}