uva 1658 Admiral(费用流+拆点)

题目链接
题目大意:给定一张图,求从1到n两条不相交路径的最小权值和。
分析:要求两条不相交路径的最小权值和,很明显跑最短路算法的话可能会有无解情况出现,且答案不一定合法,故考虑网络流算法,题目要求每个点最多被经过一次且每条边也最多被经过一次,给定每条边容量为1,每个点容量也为1,所以每个点要拆成两个点,中间边容量为1,表示每个点最多被选择一次。
然后起始点可以被经过两次,所以源点和汇点拆点后中间边容量要为2,表示可以用两次,然后从源点向汇点跑最小费用流。

#include<bits/stdc++.h>
#define PII pair<int,int>
#define x first
#define y second
#define MAIN main
using namespace std;
typedef long long LL;
const int INF=0x3f3f3f3f;
const int N=2e3+100,M=1e4+10;
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){}
};
vector<edge>E;
vector<int>g[N];
void add(int u,int v,int cap,int cost)
{
    E.push_back(edge(u,v,cap,0,cost));
    E.push_back(edge(v,u,0,0,-cost));
    int m=E.size();
    g[u].push_back(m-2);
    g[v].push_back(m-1);
}
int dis[N],vis[N],a[N],pre[N];
int n,m;
bool spfa(int s,int t,int &flow,int &cost)
{
    for(int i=1;i<=2*n;i++) dis[i]=INF,vis[i]=0;
    queue<int>q;
    q.push(s);
    vis[s]=1;
    dis[s]=0;
    a[s]=INF;
    pre[s]=0;
    while(!q.empty())
    {
        int u=q.front();q.pop();
        vis[u]=0;
        for(int i=0;i<(int)g[u].size();i++)
        {
            edge &e=E[g[u][i]];
            int v=e.v;
            if(dis[v]>dis[u]+e.cost&&e.cap>e.flow)
            {
                dis[v]=dis[u]+e.cost;
                pre[v]=g[u][i];
                a[v]=min(a[u],e.cap-e.flow);
                if(!vis[v]) vis[v]=1,q.push(v);
            }
        }
    }
    if(dis[t]==INF) return false;
    flow+=a[t];
    cost+=a[t]*dis[t];
    int now=t;
    while(now!=s)
    {
        E[pre[now]].flow+=a[t];
        E[pre[now]^1].flow-=a[t];
        now=E[pre[now]].u;
    }
    return true;
}
int mincost(int s,int t)
{
    int cost=0,flow=0;
    while(spfa(s,t,flow,cost));
    return cost;
}
int MAIN()
{
    while(scanf("%d%d",&n,&m)==2)
    {
        E.clear();
        for(int i=1;i<=2*n;i++) g[i].clear();
        for(int i=1;i<=m;i++)
        {
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            add(u+n,v,1,w);
        }
        for(int i=2;i<=n-1;i++) add(i,i+n,1,0);
        add(1,1+n,2,0);
        add(n,n+n,2,0);
        int ans=mincost(1,n+n);
        printf("%d\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值