【Luogu】P2469星际竞速(费用流)

  题目链接

  费用流,类似最小路径覆盖。

  从起点向i连一条容量1费用0的边,从i'向终点连一条容量1费用0的边;

  从起点向i'连一条容量1费用为瞬移的边,从i向j'连一条容量1费用为边权的边。

  然后跑就可以了。

#include<cstdio>
#include<cstdlib>
#include<cctype>
#include<cstring>
#include<algorithm>
#include<queue>
#define maxn 200020
using namespace std;
inline long long read(){
    long long num=0,f=1;
    char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-')    f=-1;
        ch=getchar();
    }
    while(isdigit(ch)){
        num=num*10+ch-'0';
        ch=getchar();
    }
    return num*f;
}

struct Edge{
    int next,from,to,val,dis;
}edge[maxn];
int head[maxn],num;
inline void addedge(int from,int to,int val,int dis){
    edge[++num]=(Edge){head[from],from,to,val,dis};
    head[from]=num;
}
inline void add(int from,int to,int val,int dis){
    addedge(from,to,val,dis);
    addedge(to,from,0,-dis);
}

inline int count(int i){    return i&1?i+1:i-1;    }

int dis[maxn];
int pre[maxn];
bool vis[maxn];
int Start,End;

int spfa(){
    memset(dis,127/3,sizeof(dis));    dis[Start]=0;
    memset(pre,0,sizeof(pre));
    queue<int>q;    q.push(Start);
    while(!q.empty()){
        int from=q.front();    q.pop();    vis[from]=0;
        //printf("%d\n",from);
        for(int i=head[from];i;i=edge[i].next){
            int to=edge[i].to;
            if(edge[i].val<=0||dis[to]<=dis[from]+edge[i].dis)    continue;
            dis[to]=dis[from]+edge[i].dis;
            pre[to]=i;
            if(vis[to])    continue;
            vis[to]=1;    q.push(to);
        }
    }
    if(pre[End]==0)    return 0;
    int now=End;
    while(now!=Start){
        int ret=pre[now];
        edge[ret].val--;    edge[count(ret)].val++;
        now=edge[ret].from;
    }
    return dis[End];
}

int main(){
    int n=read(),m=read();
    End=n*2+1;
    for(int i=1;i<=n;++i){
        add(Start,i+n,1,read());
        add(Start,i,1,0);
        add(i+n,End,1,0);
    }
    for(int i=1;i<=m;++i){
        int from=read(),to=read(),val=read();
        if(from>to)    swap(from,to);
        add(from,to+n,1,val);
    }
    int ans=0;
    while(1){
        int now=spfa();
        if(now==0)    break;
        ans+=now;
    }
    printf("%d",ans);
    return 0;
}

 

posted @ 2018-04-19 13:03  Konoset  阅读(203)  评论(0编辑  收藏  举报