首页 > Web开发 > 详细

[AHOI2014/JSOI2014]支线剧情(最小费用可行流)

时间:2020-03-13 12:01:28      阅读:79      评论:0      收藏:0      [点我收藏+]

[AHOI2014/JSOI2014]支线剧情(luogu)

看到“花费最少时间”等字眼可以想到建一个费用流的图

我们对于每一个剧情 i 可以通过 花费时间 t 的支线剧情 到剧情 b 的关系建一条从 i 到 b ,费用为 t 的边

然而这些边流量上界为 INF ,下界为 1

我们再从每个不为 1 的 i 向 1 连一条费用为0,流量上界为INF,下界为 0 的边

于是我们得到一张无源汇,且流量有上下界的网络流图

首先每条边下界必须流满,于是记ans为 每条边费用*流量下界 的和,再将每条边改造成下界为 0 ,上界为 原上界-原下界

而下界为 0 =无下界,所以可以开始求最小费用最大流了吗

 

 

源汇点都没有怎么求最大流!!我一定是在口胡读者

这都是小事,最大的问题是,这时求最小费用最大流可能会导致每个点真实入流!=真实出流

为了解决这个问题,我们建立源点 s ,汇点 t,

设 d[i] =点 i 在改造前的 入边流量下界和-出边流量下界和

对于每个 i ,若d[i]>0 , 从 s 向 i 连一条费用为 0 ,流量为 d[i] 的边

若d[i]<0 , 从 i 向 t 连一条费用为 0 ,流量为 -d[i] 的边

于是此时求最小费用最大流,最小费用+ans即为答案

 

Code

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <cstring>
using namespace std;
const int N=500,M=2e5;
int head[N],ver[M],nxt[M],cost[M],edge[M],tot=1;
int n,k,a,b,s,t,d[N],sum,mc,mf,dis[N],incf[N],pre[N],inf=1<<30;
bool in[N];
void add(int u,int v,int w,int c)
{
    ver[++tot]=v,nxt[tot]=head[u],edge[tot]=w,cost[tot]=c,head[u]=tot;
    ver[++tot]=u,nxt[tot]=head[v],edge[tot]=0,cost[tot]=-c,head[v]=tot;
}
bool spfa()
{
    memset(dis,0x3f,sizeof(dis));
    pre[t]=-1,pre[s]=0,dis[s]=0,incf[s]=inf;
    queue <int> q;
    q.push(s);
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        in[u]=false;
        for(int i=head[u],v;i;i=nxt[i])
            if(edge[i]>0 && dis[v=ver[i]]>dis[u]+cost[i])
            {
                dis[v]=dis[u]+cost[i];
                pre[v]=i,incf[v]=min(edge[i],incf[u]);
                if(!in[v]) in[v]=true,q.push(v);
            }
    }
    return pre[t]!=-1;
}
void update()
{
    mc+=incf[t]*dis[t],mf+=incf[t];
    int x=t;
    while(x!=s)
    {
        int i=pre[x];
        edge[i]-=incf[t],edge[i^1]+=incf[t];
        x=ver[i^1];
    }
}
int main()
{
    scanf("%d",&n);
    s=0,t=n+1;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&k);
        while(k--)
        {
            scanf("%d%d",&a,&b);
            add(i,a,inf,b);
            sum+=b;
            d[i]--,d[a]++;
        }
    }
    for(int i=2;i<=n;i++) add(i,1,inf,0);
    for(int i=1;i<=n;i++)
        if(d[i]>0) add(s,i,d[i],0);
        else if(d[i]<0) add(i,t,-d[i],0);
    while(spfa()) update();
    printf("%d",sum+mc);
    return 0;
    
}

 

[AHOI2014/JSOI2014]支线剧情(最小费用可行流)

原文:https://www.cnblogs.com/hsez-cyx/p/12484523.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!