[省选前题目整理][BZOJ 1927][SDOI 2010]星际竞速(费用流)

142 篇文章 0 订阅
98 篇文章 0 订阅

题目链接

http://www.lydsy.com/JudgeOnline/problem.php?id=1927

思路

非常好的一道最小费用最大流的题目。
题目要求在一个有边权的有向图中,经过所有的点,并且可以花费一定代价选择顺移,求最小代价。

这道题非常类似于最小路径覆盖问题,但是只是多了一个顺移。在最小路径覆盖问题中,我们把每个点拆成了入点和出点,那么对于每个点 u 及其顺移代价au而言,就从源点连容量为 au 的边到 u <script id="MathJax-Element-43" type="math/tex">u</script>的出点即可

代码

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>

#define MAXE 40000
#define MAXV 40000
#define INF 0x3f3f3f3f

using namespace std;

struct edge
{
    int u,v,w,cap,next;
}edges[MAXE];

int head[MAXV],nCount=1;

void AddEdge(int U,int V,int W,int C)
{
    edges[++nCount].u=U;
    edges[nCount].v=V;
    edges[nCount].w=W;
    edges[nCount].cap=C;
    edges[nCount].next=head[U];
    head[U]=nCount;
}

void add(int U,int V,int W,int C)
{
    AddEdge(U,V,W,C);
    AddEdge(V,U,-W,0);
}

int dist[MAXV],pre[MAXV],inQueue[MAXV],q[2*MAXE];
int S,T;

bool SPFA()
{
    memset(inQueue,false,sizeof(inQueue));
    memset(dist,INF,sizeof(dist));
    memset(pre,-1,sizeof(pre));
    int h=0,t=1;
    q[h]=S;
    dist[S]=0;
    inQueue[S]=true;
    while(h<t)
    {
        int u=q[h++];
        inQueue[u]=false;
        for(int p=head[u];p!=-1;p=edges[p].next)
        {
            int v=edges[p].v;
            if(dist[u]+edges[p].w<dist[v]&&edges[p].cap) //!!!!!!
            {
                dist[v]=dist[u]+edges[p].w;
                pre[v]=p;
                if(!inQueue[v])
                {
                    inQueue[v]=true;
                    q[t++]=v;
                }
            }
        }
    }
    return pre[T]!=-1;
}

int MCMF()
{
    int cost=0;
    while(SPFA())
    {
        int flow=INF;
        for(int p=pre[T];p!=-1;p=pre[edges[p].u])
            flow=min(flow,edges[p].cap);
        for(int p=pre[T];p!=-1;p=pre[edges[p].u])
        {
            edges[p].cap-=flow;
            edges[p^1].cap+=flow;
        }
        cost+=flow*dist[T];
    }
    return cost;
}

int n,m;

inline int in(int x)
{
    return x;
}

inline int out(int x)
{
    return x+n;
}

int main()
{
    nCount=1;
    memset(head,-1,sizeof(head));
    scanf("%d%d",&n,&m);
    S=MAXV-2;T=MAXV-1;
    for(int i=1;i<=n;i++)
    {
        int a; //顺移到i的代价为a
        scanf("%d",&a);
        add(S,out(i),a,1);
    }
    for(int i=1;i<=m;i++)
    {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        if(u>v) swap(u,v);
        add(in(u),out(v),w,1);
    }
    for(int i=1;i<=n;i++)
    {
        add(S,in(i),0,1);
        add(out(i),T,0,1);
    }
    printf("%d\n",MCMF());
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值