Codeforces237 E. Build String 最小费用最大流

Codeforces237 E. Build String 最小费用最大流


传送门: https://codeforces.com/contest/237/problem/E

题意

给 一 个 模 式 串 S 和 n 个 字 符 串 , 问 能 不 能 在 这 n 个 字 符 串 中 选 取 字 符 从 而 组 成 一 个 S 。 给一个模式串S和n个字符串,问能不能在这n个字符串中选取字符从而组成一个S。 SnnS
在 t i 中 取 一 个 字 符 的 费 用 为 i , 每 个 字 符 串 最 大 取 字 符 个 数 为 a i 。 在t_i中取一个字符的费用为i,每个字符串最大取字符个数为a_i。 tiiai

若 能 组 成 , 则 输 出 最 小 费 用 , 否 则 输 出 − 1. 若能组成,则输出最小费用,否则输出-1. 1.

思路

看 到 这 个 费 用 , 在 转 化 成 网 络 流 模 型 , 这 题 就 A 了 。 看到这个费用,在转化成网络流模型,这题就A了。 A

设点:
设 每 个 字 符 为 1 − 26 , n 个 串 的 为 26 + i ( i ϵ n ) 。 源 点 为 0 , 汇 点 为 n + 26 + 1 。 设每个字符为1-26,n个串的为26+i(i\epsilon n)。源点为0,汇点为n+26+1。 126n26+i(iϵn)0n+26+1

建边:

  • 源 点 连 接 每 个 字 符 a d d ( s , i , n u m [ i ] , 0 ) , 字 符 i 的 个 数 。 源点连接每个字符add(s,i,num[i],0),字符i的个数。 add(s,i,num[i],0)i
  • 每 个 字 符 连 接 n 个 字 符 串 a d d ( i , j + 26 , m p [ j ] [ i ] , 0 ) , 第 j 个 字 符 串 中 字 符 i 的 个 数 。 每个字符连接n个字符串add(i,j+26,mp[j][i],0),第j个字符串中字符i的个数。 nadd(i,j+26,mp[j][i],0)ji
  • n 个 字 符 串 连 接 汇 点 , a d d ( i + 26 , t , a [ i ] , j ) , 第 j 个 字 符 串 中 最 大 可 取 字 符 个 数 a [ i ] , 费 用 为 j 。 n个字符串连接汇点,add(i+26,t,a[i],j),第j个字符串中最大可取字符个数a[i],费用为j。 nadd(i+26,t,a[i],j)ja[i]j

跑 一 遍 M C M F , 判 断 是 否 满 流 , 即 m a x f l o w = n , 输 出 m i n c o s t , 否 则 输 出 − 1 。 跑一遍MCMF,判断是否满流,即maxflow=n,输出mincost,否则输出-1。 MCMFmaxflow=nmincost1

Code(62MS)

#include "bits/stdc++.h"

using namespace std;

typedef long long ll;
typedef long double ld;
typedef pair<int, int> pii;

#define endl "\n"
#define pb push_back
#define mem(a, b) memset(a , b , sizeof(a))
#define FOR(i, x, n) for(int i = x;i <= n; i++)

const int INF = 0x3f3f3f3f;
// const ll mod = 998244353;
// const ll mod = 1e9 + 7;
const double eps = 1e-6;
const double PI = acos(-1);
const double R = 0.57721566490153286060651209;

const int maxn = 1005;      //点数

struct Edge {
    int from, to, cap, flow, cost;

    Edge(int u, int v, int c, int f, int cc)
            : from(u), to(v), cap(c), flow(f), cost(cc) {}
};

struct MCMF {
    int n, m;
    vector<Edge> edges;
    vector<int> G[maxn];
    int inq[maxn];  //是否在队列中
    int d[maxn];    //bellmanford
    int p[maxn];    //上一条弧
    int a[maxn];    //可改进量
    void init(int n) {
        this->n = n;
        for (int i = 0; i <= n; ++i) G[i].clear();
        edges.clear();
    }

    void add(int from, int to, int cap, int cost) {
        edges.emplace_back(Edge(from, to, cap, 0, cost));
        edges.emplace_back(Edge(to, from, 0, 0, -cost));
        m = int(edges.size());
        G[from].emplace_back(m - 2);
        G[to].emplace_back(m - 1);
    }

    bool spfa(int s, int t, int &flow, int &cost) {
        for (int i = 1; i <= n; ++i) d[i] = INF;
        memset(inq, 0, sizeof(inq));
        d[s] = 0;
        inq[s] = 1;
        p[s] = 0;
        queue<int> q;
        a[s] = INF;
        q.push(s);
        while (!q.empty()) {
            int u = q.front();
            q.pop();
            inq[u] = 0;
            for (int i = 0; i < int(G[u].size()); ++i) {
                Edge &e = edges[G[u][i]];
                if (e.cap > e.flow && d[e.to] > d[u] + e.cost) {
                    d[e.to] = d[u] + e.cost;
                    p[e.to] = G[u][i];
                    a[e.to] = min(a[u], e.cap - e.flow);
                    if (!inq[e.to]) {
                        q.push(e.to);
                        inq[e.to] = 1;
                    }
                }
            }
        }
        if (d[t] == INF) return false;
        flow += a[t];
        cost += d[t] * a[t];
        for (int u = t; u != s; u = edges[p[u]].from) {
            edges[p[u]].flow += a[t];
            edges[p[u] ^ 1].flow -= a[t];
        }
        return true;
    }

    int MincostMaxflow(int s, int t, int &cost) {
        int flow = 0;
        cost = 0;
        while (spfa(s, t, flow, cost));
        return flow;
    }
} mcmf;

int n, m, mincost;

void solve() {
    string S; cin >> S;
    cin >> n;
    map<int, int> num, mp[n + 1];
    vector<int> a(n + 1);
    for(int i = 0;i < S.length(); i++) {
        num[S[i] - 'a' + 1]++;
    }
    for(int i = 1;i <= n; i++) {
        string T;
        cin >> T >> a[i];
        for(int j = 0;j < T.length(); j++) {
            mp[i][T[j] - 'a' + 1]++;
        }
    }
    int s = 0, t = n + 26 + 1;
    mcmf.init(t);
    for(int i = 1;i <= 26; i++) {
        if(!num[i]) continue;
        mcmf.add(s, i, num[i], 0); // 源点连接每个字符,流量为S串每个字符点个数
        for(int j = 1;j <= n; j++) {
            if(!mp[j][i]) continue;
            mcmf.add(i, j + 26, mp[j][i], j); // 每个字符连接n个字符串,流量为该字符串中i的个数,费用为行标j
        }
    }
    for(int i = 1;i <= n; i++) {
        mcmf.add(i + 26, t, a[i], 0); // 每个字符串连接汇点,流量为a[i],即最大使用次数
    }
    int maxflow = mcmf.MincostMaxflow(s, t, mincost);
    if(maxflow == S.length()) cout << mincost << endl;
    else cout << -1 << endl;
}

signed main() {
    solve();
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值