有源汇上下界最小费用可行流 ---- P4043 [AHOI2014/JSOI2014]支线剧情(模板)

题目链接


题目大意:

在这里插入图片描述


解题思路:

  1. 有源汇上下界最小费用可行流模板题目来着
  2. 先建出一个有源汇上下界可行流的图,然后注意建图的时候要把每条边的下界的费用提前加到ans里面
  3. 然后再对图跑费用流,就是补齐费用

AC code

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int maxn = 4010;
const ll INF = 0x3f3f3f3f3f3f3f3f;
int n, m;
struct node {
   int nxt, to;
   ll flow, w;
}edge[maxn * 10];
int head[maxn], cnt;
inline void add(int from, int to, int flow, int cost) {
   edge[cnt] = {head[from],to,flow,cost};
   head[from] = cnt ++;
   edge[cnt] = {head[to],from,0,-cost};
   head[to] = cnt ++;
}
ll dis[maxn], incf[maxn];
int vis[maxn], pre[maxn];
int s, t;
inline bool spfa() {
    queue<int> q;
    memset(dis,INF,sizeof(dis));
    memset(vis,0,sizeof(vis));
    q.push(s);
    dis[s] = 0;
    vis[s] = 1;
    incf[s] = 1 << 30;
    while(!q.empty()) {
        int u = q.front();
        vis[u] = 0;
        q.pop();
        for(int i = head[u]; ~i; i = edge[i].nxt) {
            if(!edge[i].flow) continue;
            int v = edge[i].to;
            if(dis[v] > dis[u] + edge[i].w) {
                dis[v] = dis[u] + edge[i].w;
                incf[v] = min(incf[u],edge[i].flow);
                pre[v] = i;
                if(!vis[v]) vis[v] = 1, q.push(v);
            }
        }
    }
    return dis[t] != INF;
}
ll ans;
inline void MCMF() {
    ll mincost = 0;
    while(spfa()) {
        int x = t;
        mincost += dis[x] * incf[x];
        int i; 
        while(x != s) {
            i = pre[x];
            edge[i].flow -= incf[t];
            edge[i^1].flow += incf[t];
            x = edge[i^1].to;
        }
    }
    ans += mincost; // 加上补的费用流
}

inline void init() {
    memset(pre,0,sizeof(pre));
    memset(incf,INF,sizeof(incf));
    memset(head,-1,sizeof(head));
    cnt = 0;
}

//.................
int in[maxn], out[maxn];
int main() {
   ios::sync_with_stdio(0);
   cin.tie(0);
   cout.tie(0);
   init();
   int n;
   cin >> n;
   int s1 = 1, t1 = n + 1, s2 = n+2, t2 = n + 3; // s1,t1是原图的源点和汇点,s2, t2是新图的源点汇点
   for(int i = 1; i <= n; ++ i) {
       int ki;
       cin >> ki;
       for(int j = 1; j <= ki; ++ j) {
           int v, w;
           cin >> v >> w;
           in[v] ++;
           out[i] ++;
           ans += 1 * w;  // 下界费用
           add(i,v,INF-1,w);
       }
   }
   for(int i = 2; i <= n; ++ i) 
     add(i,t1,INF,0); // 每个点都可以是结束点
   
   for(int i = 1; i <= n; ++ i)
     if(in[i] > out[i]) // 用自己新加的源汇点去补流
       add(s2,i,in[i]-out[i],0);
     else if(in[i] < out[i])
       add(i,t2,out[i]-in[i],0);
   
    add(t1,s1,INF,0); 
    s = s2, t = t2; // 源点汇点
    MCMF();
    cout << ans;  
   return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值