BZOJ 1927 星际竞速(费用流)

考虑费用流,题目要求走n个点都走完且恰好一次,显然流量的限制为n。

建立源点s和汇点t,并把每个星球拆成两个点i和i',分别表示已到达该点和经过该点。

对于能力爆发,建边(s,i',1,w). 对应高速航行,建边(s,i,1,0), (i,j',1,w).

因为每个点必须走一次且只能走一次。建边(i',t,1,0).

其实就是类似最小路径覆盖的建图方法。

 

# include <cstdio>
# include <cstring>
# include <cstdlib>
# include <iostream>
# include <vector>
# include <queue>
# include <stack>
# include <map>
# include <set>
# include <cmath>
# include <algorithm>
using namespace std;
# define lowbit(x) ((x)&(-x))
# define pi 3.1415926535
# define eps 1e-9
# define MOD 10000
# define INF 1000000000
# define mem(a,b) memset(a,b,sizeof(a))
# define FOR(i,a,n) for(int i=a; i<=n; ++i)
# define FO(i,a,n) for(int i=a; i<n; ++i)
# define bug puts("H");
# define lch p<<1,l,mid
# define rch p<<1|1,mid+1,r
# define mp make_pair
# define pb push_back
typedef pair<int,int> PII;
typedef vector<int> VI;
# pragma comment(linker, "/STACK:1024000000,1024000000")
typedef long long LL;
int Scan() {
    int res=0, flag=0;
    char ch;
    if((ch=getchar())=='-') flag=1;
    else if(ch>='0'&&ch<='9') res=ch-'0';
    while((ch=getchar())>='0'&&ch<='9')  res=res*10+(ch-'0');
    return flag?-res:res;
}
void Out(int a) {
    if(a<0) {putchar('-'); a=-a;}
    if(a>=10) Out(a/10);
    putchar(a%10+'0');
}
const int N=1605;
//Code begin...

struct Edge{
    int to, next, cap, flow, cost;
    Edge(int _to=0, int _next=0, int _cap=0, int _flow=0, int _cost=0):
    to(_to), next(_next), cap(_cap), flow(_flow), cost(_cost){}
}edge[40005];
struct ZKW_MinCostMaxFlow{
    int head[N], tot, cur[N], dis[N], ss, tt, n, min_cost, max_flow;
    bool vis[N];
    void init(){tot=0; mem(head,-1);}
    void addedge(int u, int v, int cap, int cost){
        edge[tot]=Edge(v,head[u],cap,0,cost);
        head[u]=tot++;
        edge[tot]=Edge(u,head[v],0,0,-cost);
        head[v]=tot++;
    }
    int aug(int u, int flow){
        if (u==tt) return flow;
        vis[u]=true;
        for (int i=cur[u]; i!=-1; i=edge[i].next) {
            int v=edge[i].to;
            if (edge[i].cap>edge[i].flow&&!vis[v]&&dis[u]==dis[v]+edge[i].cost) {
                int tmp=aug(v,min(flow,edge[i].cap-edge[i].flow));
                edge[i].flow+=tmp; edge[i^1].flow-=tmp; cur[u]=i;
                if (tmp) return tmp;
            }
        }
        return 0;
    }
    bool modify_label(){
        int d=INF;
        FO(u,0,n) if (vis[u]) for (int i=head[u]; i!=-1; i=edge[i].next) {
            int v=edge[i].to;
            if (edge[i].cap>edge[i].flow&&!vis[v]) d=min(d,dis[v]+edge[i].cost-dis[u]);
        }
        if (d==INF) return false;
        FO(i,0,n) if (vis[i]) vis[i]=false, dis[i]+=d;
        return true;
    }
    PII mincostmaxflow(int start, int end, int nn){
        ss=start, tt=end, n=nn; min_cost=max_flow=0;
        FO(i,0,n) dis[i]=0;
        while (1) {
            FO(i,0,n) cur[i]=head[i];
            while (1) {
                FO(i,0,n) vis[i]=false;
                int tmp=aug(ss,INF);
                if (tmp==0) break;
                max_flow+=tmp; min_cost+=tmp*dis[ss];
            }
            if (!modify_label()) break;
        }
        return mp(min_cost,max_flow);
    }
}solve;
int main ()
{
    int n, m, s, t, x, u, v;
    scanf("%d%d",&n,&m);
    s=0; t=2*n+1;
    solve.init();
    FOR(i,1,n) scanf("%d",&x), solve.addedge(s,i,1,0), solve.addedge(s,n+i,1,x), solve.addedge(n+i,t,1,0);
    FOR(i,1,m) {
        scanf("%d%d%d",&u,&v,&x);
        if (u>v) swap(u,v);
        solve.addedge(u,n+v,1,x);
    }
    printf("%d\n",solve.mincostmaxflow(s,t,t+1).first);
    return 0;
}
View Code

 

posted @ 2017-04-03 16:41  free-loop  阅读(185)  评论(0编辑  收藏  举报