【luogu P3381】最小费用最大流 题解

原题链接

由于题解已经烂大街了,这里只列出注意事项和部分个人难以理解的内容。

注意事项

  1. 边下标从2开始
  2. 区分流量和单价
  3. 使用dijkstra最好开long long 小心h[x]爆掉

h[x]+=dis[x]的正确性证明

代码库

SPFA

#include <cstdio>
#include <queue>
#include <algorithm>
#define REG register
#define rep(i,a,b) for(REG int i=a;i<=b;i++)
#define Rep(i,a,b) for(REG int i=a;i>=b;i--)
inline char getc(){
    static char buf[1<<14],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<14,stdin),p1==p2)?EOF:*p1++;
}
inline int scan(){
    REG int x=0; REG char ch=0;
    while(ch<48) ch=getc();
    while(ch>=48) x=x*10+ch-48,ch=getc();
    return x;
}
const int N=5e3+5,M=5e4+5,INF=2147483647;
int n,m,s,t,head[N],nex[M<<1],to[M<<1],wei[M<<1],cost[M<<1];
inline void addEdge(int u,int v,int w,int c){
    static int cc=1; //差点又忘了
    nex[++cc]=head[u]; head[u]=cc; 
    to[cc]=v; wei[cc]=w; cost[cc]=c;
}
int h[N],dis[N],pre[N],prE[N]; 
bool vis[N]; std::queue<int> Q; 
inline bool SPFA(){
    //static int err=0;
    //err++; if(err>10) return 0;
    rep(i,1,n) dis[i]=INF,vis[i]=pre[i]=prE[i]=0;
    Q.push(s); dis[s]=0; vis[s]=1;
    //printf("order:\n");
    while(!Q.empty()){
        REG int u=Q.front(); Q.pop(); vis[u]=0;
        if(u==t) continue; 
        //到t就行了 注意这里不能break 因为只是搜到了t不代表dis[t]已经最小
        for(REG int i=head[u];i;i=nex[i]){
            if(!wei[i]) continue;
            //printf("%d->%d %d cmp %d\n",u,to[i],dis[u]+cost[i],dis[to[i]]);
            //一条增广路的流量是相同的 所以求出的单位价格之和最小即可
            if(dis[u]+cost[i]<dis[to[i]]){
                dis[to[i]]=dis[u]+cost[i];
                pre[to[i]]=u; prE[to[i]]=i;
                if(!vis[to[i]]) vis[to[i]]=1,Q.push(to[i]);
            }
        }
    }
    //putchar('\n');
    return pre[t];
}
int Mflow,Mcost;
inline void calc(){
    REG int u,flow;
    for(u=t,flow=INF;pre[u];u=pre[u]) flow=std::min(flow,wei[prE[u]]);
    Mflow+=flow; Mcost+=dis[t]*flow;
    for(u=t;pre[u];u=pre[u]) wei[prE[u]]-=flow,wei[prE[u]^1]+=flow;
    //printf("ss\n");
}
int main(){
    //freopen("P3381_8.in","r",stdin);
    n=scan(),m=scan(),s=scan(),t=scan();
    REG int u,v,w,c;
    while(m--){
        u=scan(),v=scan(),w=scan(),c=scan();
        addEdge(u,v,w,c); addEdge(v,u,0,-c);
    }
    while(SPFA()) calc();
    printf("%d %d\n",Mflow,Mcost);
    return 0;
}

dijkstra

#include <cstdio>
typedef long long ll;
#define REG register
#define rep(i,a,b) for(REG int i=a;i<=b;i++)
#define Rep(i,a,b) for(REG int i=a;i>=b;i--)
inline char getc(){
    static char buf[1<<14],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<14,stdin),p1==p2)?EOF:*p1++;
}
inline int scan(){
    REG int x=0; REG char ch=0;
    while(ch<48) ch=getc();
    while(ch>=48) x=x*10+ch-48,ch=getc();
    return x;
}
inline ll min(const ll&a,const ll&b){ return a<b?a:b; }
template<typename T> inline void swap(T &a,T &b){ REG T t=a; a=b; b=t; }
const int N=5e3+5,M=5e4+5; const ll INF=1e18;
struct Heap{
    struct node{
        int i,siz; ll d; node *ch[2];
        node(int a=0,ll b=0):i(a),d(b),siz(1){ ch[0]=ch[1]=NULL; }
        inline void maintain(){
            siz=1; 
            if(ch[0]) siz+=ch[0]->siz;
            if(ch[1]) siz+=ch[1]->siz;
        } 
    }*root;
    inline void get_top(int &a,ll &b){ a=root->i; b=root->d; }
    inline int _siz(const node *u){ return u?u->siz:0; }
    inline ll _dis(const node *u){ return u?u->d:INF; }
    inline void _insert(node*&u,int i,ll d){
        if(!u) u=new node(i,d);
        else{
            if(u->d>d) swap(u->d,d),swap(u->i,i);
            REG int judge=_siz(u->ch[0])-_siz(u->ch[1]);
            _insert(u->ch[judge>0?1:0],i,d); 
            u->maintain();
        }
    }
    inline void _delete(node*&u){
        if(u->ch[0]||u->ch[1]){
            REG int j=_dis(u->ch[0])-_dis(u->ch[1])>0?1:0;
            swap(u->d,u->ch[j]->d); swap(u->i,u->ch[j]->i);
            _delete(u->ch[j]); u->maintain();
        }else{ delete u; u=NULL; }
    }
}Q;
int n,s,t,head[N],nex[M<<1],to[M<<1]; ll wei[M<<1],cost[M<<1];
inline void addEdge(int u,int v,int w,int c){
    static int cc=1; 
    nex[++cc]=head[u]; head[u]=cc;
    to[cc]=v; wei[cc]=w; cost[cc]=c;
} 
ll h[N],dis[N],Mcost,Mflow; int pre[N],preE[N]; bool vis[N];
inline bool dijkstra(){
    //static int cnt=0;
    //if((++cnt)&&Mflow>2938) return printf("%d\n",cnt),0;
    rep(i,1,n) dis[i]=INF,pre[i]=preE[i]=vis[i]=0;
    Q._insert(Q.root,s,0); dis[s]=0;
    while(Q.root){
        REG int u; REG ll d; 
        Q.get_top(u,d); Q._delete(Q.root);
        if(d>dis[u]||vis[u]||u==t) continue;
        vis[u]=1; 
        for(REG int i=head[u];i;i=nex[i]){
            if(!wei[i]||vis[to[i]]) continue;
            REG ll eff=cost[i]+h[u]-h[to[i]];
            if(dis[u]+eff<dis[to[i]]){
                dis[to[i]]=dis[u]+eff;
                pre[to[i]]=u; preE[to[i]]=i;
                Q._insert(Q.root,to[i],dis[to[i]]);
            }
        }
    }
    return pre[t];
}
inline void calc(){
    REG ll per_cost=dis[t]-h[s]+h[t],flow=INF;
    //注意这里的wei[...]
    for(REG int i=t;pre[i];i=pre[i]) flow=min(flow,wei[preE[i]]);
    Mflow+=flow; Mcost+=flow*per_cost;
    for(REG int i=t;pre[i];i=pre[i]) wei[preE[i]]-=flow,wei[preE[i]^1]+=flow;
    rep(i,1,n) h[i]+=dis[i];
}
int main(){
    //freopen("P3381_8.in","r",stdin);
    REG int m,u,v,w,c;
    n=scan(),m=scan(),s=scan(),t=scan(); 
    while(m--){
        u=scan(),v=scan(),w=scan(),c=scan();
        addEdge(u,v,w,c); addEdge(v,u,0,-c);
    }
    //printf("ss");
    while(dijkstra()) calc();
    printf("%lld %lld\n",Mflow,Mcost);
    return 0;
}
posted @ 2020-10-14 23:22  喵乖乖喵  阅读(213)  评论(0编辑  收藏  举报

膜拜众神

网安院技术部     ZZY大师     Xinyang 大佬     Wjyyy