[lnsyoj2610/luoguP2762] 太空飞行计划问题

题意

\(n\) 种实验,\(m\) 种器材,第 \(i\) 种实验需要器材集合为 \(R_i\),完成第 \(i\) 种实验可以获得 \(w_i\) 元,使用第 \(i\) 种器材需要 \(c_i\) 元,求最大利润。

sol

将实验作为左部点,器材作为右部点,根据依赖关系连边,左部连边奖金,右部连边成本。
记选择做的实验收益和为 \(A\),选择不做的实验收益和为 \(B\),选择的器材成本为 \(a\),不选择的器材成本为 \(b\),则最小割为 \(B + a\),那么使用总收益-最小割即为 \(A + B - B -a=A-a\)

代码

#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>

using namespace std;

const int N = 105, M = 6005, INF = 0x3f3f3f3f;

int h[N], e[M], cap[M], ne[M], idx;
int d[N], cur[N];
int n, S, T;
int nn, m;
int exprc[N], expr[N][N], cost[N];
char s[205];
bool st[N];

void add(int a, int b, int c){
    e[idx] = b, cap[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
    e[idx] = a, cap[idx] = 0, ne[idx] = h[b], h[b] = idx ++ ;
}

void build(){
    memset(h, -1, sizeof h);
    n = nn + m + 2;
    S = n - 1, T = n;
    for (int i = 1; i <= nn; i ++ ) add(S, i, exprc[i]);
    for (int i = 1; i <= m; i ++ ) add(nn + i, T, cost[i]);
    for (int i = 1; i <= nn; i ++ )
        for (int j = 1; j <= expr[i][0]; j ++ )
            add(i, nn + expr[i][j], INF);
}

void print() {
    for (int i = 1; i <= nn; i ++ ) if (~d[i]) {
        printf("%d ", i);
        for (int j = 1; j <= expr[i][0]; j ++ )
            st[expr[i][j]] = true;
    }
    puts("");
    for (int i = 1; i <= m; i ++ )
        if (st[i])
            printf("%d ", i);
    puts("");
}

bool bfs(){
    queue<int> q;
    memset(d, -1, sizeof d);
    d[S] = 0, q.push(S), cur[S] = h[S];
    while (!q.empty()) {
        int t = q.front();
        q.pop();
        for (int i = h[t]; ~i; i = ne[i]){
            int j = e[i];
            if (d[j] == -1 && cap[i]) {
                d[j] = d[t] + 1;
                cur[j] = h[j];
                if (j == T) return true;;
                q.push(j);
            }
        }
    }
    return false;
}

int find(int u, int limit){
    if (u == T) return limit;
    int flow = 0;
    for (int i = cur[u]; ~i && flow < limit; i = ne[i]){
        int j = e[i];
        cur[u] = i;
        if (d[j] == d[u] + 1 && cap[i]){
            int t = find(j, min(cap[i], limit - flow));
            if (!t) d[t] = -1;
            cap[i] -= t; cap[i ^ 1] += t; flow += t;
        }
    }
    return flow;
}

int dinic(){
    int r = 0, flow;
    while (bfs()) while (flow = find(S, INF)) r += flow;
    return r;
}

int main(){
    scanf("%d%d", &nn, &m);
    int sum = 0;
    for (int i = 1; i <= nn; i ++ ) {
        scanf("%d", &exprc[i]);
        sum += exprc[i];
        memset(s, 0, sizeof s);
        cin.getline(s, 200);
        int ulen = 0, t;
        while (sscanf(s + ulen, "%d", &t) == 1) {
            expr[i][ ++ expr[i][0]] = t;
            if (!t) ulen ++ ;
            else 
                while (t) t /= 10, ulen ++ ;
            ulen ++ ;
        }
    }
    for (int i = 1; i <= m; i ++ ) scanf("%d", &cost[i]);

    build();

    int t = dinic();

    print();
    printf("%d\n", sum - t);
}
posted @ 2025-02-06 17:03  是一只小蒟蒻呀  阅读(7)  评论(0)    收藏  举报