【BZOJ1927】星际竞速(SCOI2010)-费用流+拆点

测试地址:星际竞速
做法:本题需要用到费用流+拆点。
分析题目中要求的路径,我们发现在一条满足要求的路径中,除起点那颗不和任何点连通的点外,所有点的入度都是 1 1 ,那么这些入度从哪里来呢?一是从某个星球飞过来,二是从某个星球跳过来(这话说的……)。因为一开始我们也是从一个星球跳到某个星球上的,所以我们把所有跳的决策都看做从起点跳过去,那么每个点的入度来源就只有两个,一是从某个星球飞过来,二是从起点跳过来。因为路径上每个点的出度也只有1,那么我们显然可以把一个点拆成两个点,一个表示出度点,一个表示入度点。那么我们可以这样建图:
从源点向每个出度点连容量为 1 1 ,费用为0的边,再从源点向每个入度点连容量为 1 1 ,费用为跳跃到该点的定位时间,然后从每个入度点向汇点连容量为1,费用为 0 0 的边,最后对于原来一条可以走的边(x,y),从 x x 的出度点向y的入度点连容量为 1 1 <script type="math/tex" id="MathJax-Element-1055">1</script>,费用为所需时间的边。这样每一份流量都代表一次决策,即从某个星球飞向另一个星球,或直接跳跃到某个星球,那么在最大流的情况下能得到的最小费用就是我们要求的答案了,所以直接做即可。
以下是本人代码:

#include <bits/stdc++.h>
using namespace std;
const int inf=1000000000;
int n,m,S,T,first[2010]={0},tot=1;
int dis[2010],laste[2010],last[2010];
queue<int> Q;
bool vis[2010];
struct edge
{
    int v,next,f,c;
}e[50010];

void insert(int a,int b,int f,int c)
{
    e[++tot].v=b,e[tot].next=first[a],e[tot].f=f,e[tot].c=c,first[a]=tot;
    e[++tot].v=a,e[tot].next=first[b],e[tot].f=0,e[tot].c=-c,first[b]=tot;
}

void init()
{
    scanf("%d%d",&n,&m);
    S=(n<<1)+1,T=(n<<1)+2;
    for(int i=1;i<=n;i++)
    {
        int t;
        scanf("%d",&t);
        insert(S,i,1,0);
        insert(S,n+i,1,t);
        insert(n+i,T,1,0);
    }
    for(int i=1;i<=m;i++)
    {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        if (u>v) swap(u,v);
        insert(u,n+v,1,w);
    }
}

bool spfa()
{
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=T;i++)
        dis[i]=inf;
    dis[S]=0;
    vis[S]=1;
    Q.push(S);
    while(!Q.empty())
    {
        int v=Q.front();Q.pop();
        for(int i=first[v];i;i=e[i].next)
            if (e[i].f&&dis[e[i].v]>dis[v]+e[i].c)
            {
                laste[e[i].v]=i;
                last[e[i].v]=v;
                dis[e[i].v]=dis[v]+e[i].c;
                if (!vis[e[i].v]) Q.push(e[i].v),vis[e[i].v]=1;
            }
        vis[v]=0;
    }
    return dis[T]!=inf;
}

void mincost()
{
    int minc=0;
    while(spfa())
    {
        int x=T,maxf=inf;
        while(x!=S)
        {
            maxf=min(maxf,e[laste[x]].f);
            x=last[x];
        }
        x=T;
        while(x!=S)
        {
            e[laste[x]].f-=maxf;
            e[laste[x]^1].f+=maxf;
            x=last[x];
        }
        minc+=maxf*dis[T];
    }
    printf("%d",minc);
}

int main()
{
    init();
    mincost();

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值