zoukankan      html  css  js  c++  java
  • bzoj1927: [Sdoi2010]星际竞速

    最小费用最大流。

    拆点法建模,一个点分为u0,u1。

    虚拟源点S与每个u0连一条容量为1,费用为a[u]的边,与每个u1连一条容量为1,费用为0的边。

    每个u0与虚拟汇点T连一条容量为1,费用为0的边。

    每个u能到达v的点u1和v0连一条容量为1,费用为w的边。

    为什么?

    1.如果一个点是用瞬间移动到达的,就相当与从S点瞬间移动。

    2.如果一个点是航路连去的,相当于从另一个点的u1连过去,因为s流到u1没有费用,所以费用就是w。

    建模太巧妙了。

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    const int maxn = 1600 + 10;
    const int maxm = 300000 + 10;
    const int INF = 0x3f3f3f3f;
    
    int n,m;
    int g[maxn],v[maxm],next[maxm],f[maxm],d[maxm],eid=0,vid=0;
    int id[maxn][2],q[maxm],pre[maxn],l,r,u;
    int dist[maxn];
    bool inque[maxn];
    int S,T;
    
    void addedge(int a,int b,int c,int D) {
        v[eid]=b; next[eid] = g[a]; f[eid]=c; d[eid]=D;  g[a]=eid++;
        v[eid]=a; next[eid] = g[b]; f[eid]=0; d[eid]=-D; g[b]=eid++;
    }
        
    
    void build() {
        memset(g,-1,sizeof(g));
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++) id[i][0] = ++vid,id[i][1]=++vid;
        S=++vid; T=++vid;
        for(int i=1,a;i<=n;i++) {
            scanf("%d",&a);
            addedge(S,id[i][1],1,0);
            addedge(S,id[i][0],1,a);
            addedge(id[i][0],T,1,0);
        }
        for(int i=1,u,v,w;i<=m;i++) {
            scanf("%d%d%d",&u,&v,&w);
            if(u>v) swap(u,v);
            addedge(id[u][1],id[v][0],1,w);    
        }
    }
    
    bool spfa() {
        memset(dist,0x3f,sizeof(dist));
        l=r=0; dist[q[r++]=S]=0;
        while(l<r) {
            inque[u=q[l++]]=0;
            for(int i=g[u];~i;i=next[i]) 
                if(f[i] && dist[v[i]] > dist[u]+d[i]) {
                    dist[v[i]] = dist[u]+d[i];
                    pre[v[i]]=i;            
                    if(!inque[v[i]]) inque[q[r++]=v[i]]=1;
            }
        }
        return dist[T]<INF;
    }
    
    int augment() {
        int aug=INF,res=0;
        for(int i=T;i!=S;i=v[pre[i]^1]) {
            aug=min(aug,f[pre[i]]);
        }
        for(int i=T;i!=S;i=v[pre[i]^1])
            f[pre[i]]-=aug,f[pre[i]^1]+=aug,res+=aug*d[pre[i]];
        
        return res;
    }
    
    void solve() {
        int res=0;
        while(spfa()) 
            res+=augment();
        printf("%d
    ",res);
    }
    
    int main() {
        build();
        solve();
        return 0;
    }
  • 相关阅读:
    PHP中字符串和正则表达式的常用函数
    PHP基础语法
    vscode新手心得
    socket数据通讯
    PHP 登录类
    PHP Session 封装类
    php 邮箱验证函数
    PHP MySql 分页实例
    Bootstrap
    projact mail
  • 原文地址:https://www.cnblogs.com/invoid/p/5436565.html
Copyright © 2011-2022 走看看