图论习题集

1.F (codeforces.com)

        (1)题目大意

                给你一个无向图,你需要修改成有向图,问你修改完之后每一个点入度-a[i]的和的最小是多少。

         (2)解题思路

                对于每一条边,我们给他抽象成一个点,这个题就转变为了最小费用最大流模型,考虑建边。

                对于第i条边L,R,首先有一条从源点到i容量为1,费用为0的管道

                对于L,有一条从i到m+L容量为1,费用为0的管道

                对于R,有一条从i到m+R容量为1,费用为0的管道

                对于每一个点,我们都有从当前点向汇点的容量为a[i],费用为0的管道

                对于每一个点,我们都有从当前点向汇点的容量为INF,费用为1的管道

                然后跑一个EK算一下最小费用流即可。

         (3)代码实现

#include <iostream>
#include <map>
#include <set>
#include <vector>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <stack>
#include <algorithm>
#include <cmath>
#include <unordered_map>
#include <deque>
#include <bitset>
#define rep(i,z,n) for(int i = z;i <= n; i++)
#define per(i,n,z) for(int i = n;i >= z; i--)
#define PII pair<int,int>
#define fi first
#define se second
#define vi vector<int>
#define vl vector<ll>
#define pb push_back
#define sz(x) (int)x.size()
#define all(x) (x).begin(),(x).end()
using namespace std;
using ll = long long;
const int N = 3210;
int he[N],w[N],id[N],pre[N],dis[N],mf[N],vis[N],idx;
struct Edge {
    int nt,to;
    int rl,fw;
}E[N];
int S,T,maxf,minv;
int n,m;
const int inf = 0x3f3f3f3f;
void add(int a,int b,int rl,int fw)
{
    E[idx] = {he[a],b,rl,fw};
    he[a] = idx ++;

    E[idx] = {he[b],a,0,-fw};
    he[b] = idx ++;
}
bool spfa()
{
    rep(i,0,T) {
        vis[i] = mf[i] = 0;
        dis[i] = inf;
    }
    dis[S] = 0,mf[S] = inf;
    queue <int> q;
    q.push(S);
    while(sz(q)) {
        int v = q.front();
        q.pop();
        vis[v] = false;
        for(int i = he[v];~i;i = E[i].nt) {
            int j = E[i].to,W = E[i].fw,rl = E[i].rl;
            if(dis[j] > dis[v] + W && rl) {
                dis[j] = dis[v] + W;
                mf[j] = min(mf[v],rl);
                pre[j] = i;
                if(!vis[j]) {
                    vis[j] = true;
                    q.push(j);
                }
            }
        }
    }
    return mf[T] > 0;
}
void EK()
{
    while(spfa()) {
        for(int V = T;V != S;) {
            int i = pre[V];
            E[i].rl -= mf[T];
            E[i ^ 1].rl += mf[T];
            V = E[i ^ 1].to;
        }
        maxf += mf[T];
        minv += dis[T] * mf[T];
    }
}
void solve()
{

    cin >> n >> m;
    memset(he,-1,sizeof(he));
    rep(i,1,n) cin >> w[i];
    maxf = 0,minv = 0;
    //把边抽象成点
    idx = 0,S = 0,T = n + m + 1;
    int L,R;
    rep(i,1,m) {
        add(S,i,1,0);
        id[i] = idx;
        cin >> L >> R;
        add(i,m + L,1,0);
        add(i,m + R,1,0);
    }
    rep(i,1,n) {
        add(i + m,T,w[i],0);
        add(i + m,T,inf,1);
    }
    EK();
    cout << minv << '\n';
    rep(i,1,m) {
        int k = id[i];
        if(E[k].rl == 0) cout << 1;
        else cout << 0;
    }
    cout << '\n';
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    int T = 1;
    cin >> T;
    while(T --) solve();
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值