[SDOI2010] 星际竞速

本文详细解析了一道经典的最小费用流问题,通过构建一个特殊的网络流模型,利用SPFA算法求解最小费用最大流,实现了求解跑完N个点的最小时间。代码示例清晰展示了如何设置网络流模型中的节点、边及其费用。

题目描述:

求跑完N个点的最小时间

题目分析:

神似最短路径覆盖
看到每个点经过一次,那就是cap的限制
花费即费用流
考虑如何建模.
先拆点
S-i的出点容量为1,花费为爆发费用
i的出点-T容量为1,花费为0
S向i的入点连容量为1,花费为0的边
u->v的出点连容量为1,花费为高速航行的费用
考虑由于求最大流,所以所有点的出点向汇点的连边都会流满。每个点的出点的到达方式即为此点的到达方式:

1.由源点流来:指在某一时刻瞬移到该星球。

2.由入点流来:指由其他星球沿航路到该星球。

流到该点出点的某入点对应的星球,在之前的某一时刻一定由某种合法方式达到过,追溯到头一定是某个瞬移到的点(因为图中没有环),“追溯”的过程就是这一条路径。注意为什么源点会向所有入点连费用为0的边,这条边的容量其实对应着这个点的出点到达后而产生的贡献。每个点的入点只连向它能到的出点,又由于图为DAG,所以一定存在必须瞬移(走源点到出点的边)的点,正确性也就显然了。换句话说,每个从源点直接流向出点的点是一条路径的起始点,它的入点因为出点的到达而有意义,之后就是整条路径的递推

题目链接:

BZOJ 1972
Luogu 2469

Ac 代码:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#define inf 0x7ffffff
const int maxm=20100;
int head[maxm],to[maxm<<1],net[maxm<<1],cost[maxm<<1],cap[maxm<<1];
int cnt=1;
int n,m;
inline void add(int u,int v,int c1,int c2){cnt++;to[cnt]=v,cap[cnt]=c1,cost[cnt]=c2,net[cnt]=head[u],head[u]=cnt;}
inline void addedge(int u,int v,int c1,int c2){add(u,v,c1,c2),add(v,u,0,-c2);}
namespace MCMF{
    int dis[maxm],flow[maxm],id[maxm],pre[maxm];
    int maxflow,mincost;
    bool vis[maxm];
    std::queue <int> dl;
    inline bool SPFA(int s,int t)
    {
        memset(dis,127/3,sizeof(dis));
        memset(pre,-1,sizeof(pre));
        vis[s]=1,dl.push(s),pre[s]=0,dis[s]=0,flow[s]=inf;
        while(!dl.empty())
        {
            int now=dl.front();
            dl.pop();
            vis[now]=0;
            for(int i=head[now];i;i=net[i])
            if(cap[i]&&dis[to[i]]>dis[now]+cost[i])
            {
                dis[to[i]]=dis[now]+cost[i];
                pre[to[i]]=now;
                id[to[i]]=i;
                flow[to[i]]=std::min(cap[i],flow[now]);
                if(!vis[to[i]]) dl.push(to[i]),vis[to[i]]=1;
            }
        }
        return pre[t]!=-1;
    }
    inline void change_cap(int s,int t,int x)
    {
        int now=t;
        while(now!=s)
        {
            cap[id[now]]-=x;
            cap[id[now]^1]+=x;
            now=pre[now];
        }
    }
    inline int mcmf(int s,int t)
    {
        maxflow=mincost=0;
        while(SPFA(s,t))
        {
            maxflow+=flow[t];
            mincost+=flow[t]*dis[t];
            change_cap(s,t,flow[t]);
        }
        return mincost;
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    int s=0,t=2*n+10;
    for(int i=1;i<=n;i++)
    {
        int x;
        scanf("%d",&x);
        addedge(s,i,1,0);
        addedge(s,i+n,1,x);
        addedge(i+n,t,1,0);
    }
    for(int i=1;i<=m;i++)
    {
        int u,v,c;
        scanf("%d%d%d",&u,&v,&c);
        addedge(std::min(u,v),std::max(u,v)+n,1,c);
    }
    printf("%d\n",MCMF::mcmf(s,t));
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值