[BZOJ1927][SDOI2010]星际竞速 最小费用最大流

用流量限制次数,用费用限制距离。
这一题和最小路径覆盖很像,所有边容量为1,把每个点拆成 u u,原图上 u v连成 u v,费用为距离,每个 u 连向汇点,费用0,源点连向每个 u ,费用0,源点连向每个u,费用为瞬移代价,直接跑最小费用最大流就可以了。
注意最小费用最大流每个点记的是来边而不是来点。

#include<ctime>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<cassert>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<sstream>
#include<climits>
#define X first
#define Y second
#define DB double
#define lc now<<1
#define rc now<<1|1
#define MP make_pair
#define LL long long
#define pb push_back
#define sqr(_) ((_)*(_))
#define INF 0x3f3f3f3f
#define pii pair<int,int>
#define pdd pair<DB,DB>
#define ull unsigned LL
#define DEBUG(...) fprintf(stderr,__VA_ARGS__)
using namespace std;
const int MAXN=2000;
template<typename T>void Read(T& x)
{
    x=0;int flag=0,sgn=1;char c;
    while(c=getchar())
    {
        if(c=='-')sgn=-1;
        else if(c>='0'&&c<='9')x*=10,x+=c-'0',flag=1;
        else if(flag)break;
    }
    x*=sgn;
}
int w[MAXN],n,m;
struct Edge{
    int u,v,cap,flow,cost;
    Edge(int u=0,int v=0,int cap=0,int flow=0,int cost=0):
        u(u),v(v),cap(cap),flow(flow),cost(cost){}
};
struct Mincost{
    vector<int> G[MAXN];
    vector<Edge> edges;
    int p[MAXN],inq[MAXN],d[MAXN],a[MAXN],S,T;
    void add(int u,int v,int cap,int cost)
    {
        edges.pb(Edge(u,v,cap,0,cost));
        edges.pb(Edge(v,u,0,0,-cost));
        int k=edges.size();
        G[u].pb(k-2);
        G[v].pb(k-1);
    }
    void init()
    {
        memset(p,0,sizeof(p));
        memset(inq,0,sizeof(inq));
        memset(a,0,sizeof(a));
        memset(d,INF,sizeof(d));
    }
    bool SPFA(int& flow,int& cost)
    {
        init();
        queue<int> q;
        q.push(S);
        inq[S]=1;
        d[S]=0;
        a[S]=INF;
        while(!q.empty())
        {
            int u=q.front();q.pop();
            inq[u]=0;
            for(int i=0;i<G[u].size();i++)
            {
                Edge& e=edges[G[u][i]];
                if(d[e.v]>d[u]+e.cost&&e.cap>e.flow)
                {
                    d[e.v]=d[u]+e.cost;
                    p[e.v]=G[u][i];
                    a[e.v]=min(a[u],e.cap-e.flow);
                    if(!inq[e.v])
                    {
                        q.push(e.v);
                        inq[e.v]=1;
                    }
                }
            }
        }
        if(d[T]==INF)return 0;
        flow+=a[T];
        cost+=d[T]*a[T];
        int u=T;
        while(u!=S)
        {
            edges[p[u]].flow+=a[T];
            edges[p[u]^1].flow-=a[T];
            u=edges[p[u]].u;
        }
        return 1;
    }
    int mincost()
    {
        int flow=0,cost=0;
        while(SPFA(flow,cost));
        return cost;
    }
}Graph;
int main()
{
#ifndef ONLINE_JUDGE
    freopen("speed.in","r",stdin);
    freopen("speed.out","w",stdout);
#endif
    Read(n);Read(m);
    for(int i=1;i<=n;i++)
        Read(w[i]);
    for(int i=1;i<=m;i++)
    {
        int a,b,c;
        Read(a),Read(b),Read(c);
        if(a>b)swap(a,b);
        Graph.add(a,b+n,1,c);
    }
    Graph.S=2*n+1,Graph.T=2*n+2;
    for(int i=1;i<=n;i++)
    {
        Graph.add(Graph.S,i,1,0);
        Graph.add(i+n,Graph.T,1,0);
        Graph.add(Graph.S,i+n,1,w[i]);
    }
    cout<<Graph.mincost()<<endl;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值