bzoj 2324: [ZJOI2011]营救皮卡丘

本文介绍了一种结合最短路径与网络流算法解决特定旅行路线问题的方法。该问题要求k个人依次经过所有点找到总花费最小的方案。通过构建特殊的网络流模型并利用弗洛伊德算法进行费用计算,最终求解出最小费用流。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题面

题意

有k个人从0点开始走,他们分开行动,要依此经过所有点,则最短距离是多少.

做法

洛谷 P1251 餐巾计划问题的做法相似.
首先所有点都必须要经过,因此我们可以看做一个人第一次到达i点时,直接到达超级汇点,又从超级源点流出到达i点,这样可以保证每个点都被经过,可以将每个点拆成两个,一个与汇点相连,一个与源点相连,流量均为1,费用为0.
为了保证依次经过,在弗洛伊德时,注意更新时不能用编号较大点更新较小点,两点间距离即为费用.

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define N 360
#define ll long long
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;

ll n,m,k,mm[N][N],ans,bb=1,first[N],s,t,last[N],B[N],d[N],mf;
bool in[N];
struct Bn
{
    ll to,next,quan,cst;
} bn[1001000];
queue<ll>que;

inline void add(ll u,ll v,ll w,ll z)
{
    bb++;
    bn[bb].to=v;
    bn[bb].next=first[u];
    bn[bb].quan=w;
    bn[bb].cst=z;
    first[u]=bb;
}

inline void ad(ll u,ll v,ll w,ll z)
{
    add(u,v,w,z);
    add(v,u,0,-z);
}

inline bool bfs()
{
    ll p,q,mn=INF;
    for(; !que.empty(); que.pop());
    memset(d,0x3f,sizeof(d));
    que.push(s);
    d[s]=0;
    for(; !que.empty();)
    {
        q=que.front();
        que.pop();
        in[q]=0;
        for(p=first[q]; p!=-1; p=bn[p].next)
        {
            if(d[bn[p].to]>d[q]+bn[p].cst&&bn[p].quan)
            {
                d[bn[p].to]=d[q]+bn[p].cst;
                last[bn[p].to]=q;
                B[bn[p].to]=p;
                if(!in[bn[p].to])
                {
                    in[bn[p].to]=1;
                    que.push(bn[p].to);
                }
            }
        }
    }
    if(d[t]==INF) return 0;
    for(p=t; p!=s; p=last[p])
    {
        mn=min(mn,bn[B[p]].quan);
    }
    ans+=mn*d[t];
    for(p=t; p!=s; p=last[p])
    {
        bn[B[p]].quan-=mn;
        bn[B[p]^1].quan+=mn;
    }
    return 1;
}

int main()
{
    memset(mm,0x3f,sizeof(mm));
    memset(first,-1,sizeof(first));
    ll i,j,ii,p,q,o,jj;
    cin>>n>>m>>k;
    for(i=1; i<=n; i++)mm[i][i]=0;
    for(i=1; i<=m; i++)
    {
        scanf("%lld%lld%lld",&p,&q,&o);
        mm[p][q]=mm[q][p]=min(mm[p][q],o);
    }
    for(ii=0; ii<=n; ii++)
    {
        for(i=0; i<=n; i++)
        {
            for(j=0; j<=n; j++)
            {
                if(ii<=i||ii<=j)
                mm[i][j]=mm[j][i]=min(mm[i][j],mm[i][ii]+mm[ii][j]);
            }
        }
    }
    for(i=0;i<=n;i++)
    {
        for(j=i+1;j<=n;j++)
        {
            if(mm[i][j]!=INF)
            {
                ad(i,j+n,1,mm[i][j]);
            }
        }
    }
    s=n*2+1,t=n*2+2;
    ad(s,0,k,0);
    for(i=1;i<=n;i++) ad(s,i,1,0),ad(i+n,t,1,0);
    for(; bfs(););
    cout<<ans;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值