欢迎光临
我们一直在努力

UVa 13008 Exposing Corruption

题目描述

Nlognia\\texttt{Nlognia}Nlognia 国家,中央委员会由许多议员组成。政治体系是二元的,每个议员属于两个政党之一: Deadly Serious Party\\texttt{Deadly Serious Party}Deadly Serious PartyDSP \\texttt{DSP }DSP )和 Party! Party! Party\\texttt{Party! Party! Party}Party! Party! PartyPPP\\texttt{PPP}PPP )。爱德华是一名调查记者,他发现议员们是腐败的:如果给予一定数量的 Nlognmoney\\texttt{Nlognmoney}Nlognmoney ,他们就会改变政党。每个议员都有自己的特定价格,但每个人都有价格。此外,某些议员之间存在敌对关系。敌对议员绝不会接受属于同一个政党。

爱德华有一笔预算,希望用它让一些议员改变政党,从而为调查收集证据。在此过程中,他必须尊重敌对关系:在所有接受金钱的议员改变政党后,敌对议员必须分属不同的政党。

爱德华希望造成最大的影响。对于每个测试用例,需要回答两个问题:

  • 使用最多全部预算,可以使得最终属于 DSP\\texttt{DSP}DSP 的议员的最大数量是多少?
  • 同样条件下,可以使得最终属于 PPP\\texttt{PPP}PPP 的议员的最大数量是多少?
  • 输入格式

    每个测试用例的格式如下:

    • 第一行:四个整数 DDD, PPP, RRR, BBB ,分别表示初始属于 DSP\\texttt{DSP}DSP 的议员数量( 1≤D≤1001 \\leq D \\leq 1001D100 )、初始属于 PPP\\texttt{PPP}PPP 的议员数量( 1≤P≤1001 \\leq P \\leq 1001P100 )、敌对关系数量( 1≤R≤20001 \\leq R \\leq 20001R2000 )和预算( 1≤B≤1041 \\leq B \\leq 10^{4}1B104 )。
    • 第二行: DDD 个整数 S1,S2,…,SDS_1, S_2, …, S_DS1,S2,,SD ,表示 DSP\\texttt{DSP}DSP 议员 iii 改变政党所需的价格( 1≤Si≤1001 \\leq S_i \\leq 1001Si100 )。
    • 第三行: PPP 个整数 T1,T2,…,TPT_1, T_2, …, T_PT1,T2,,TP ,表示 PPP\\texttt{PPP}PPP 议员 jjj 改变政党所需的价格( 1≤Tj≤1001 \\leq T_j \\leq 1001Tj100 )。
    • 接下来 RRR 行:每行两个整数 XXXYYY ,表示 DSP\\texttt{DSP}DSP 议员 XXXPPP\\texttt{PPP}PPP 议员 YYY 是敌对的( 1≤X≤D1 \\leq X \\leq D1XD, 1≤Y≤P1 \\leq Y \\leq P1YP )。

    输出格式

    对每个测试用例,输出一行两个整数:最大 DSP\\texttt{DSP}DSP 人数和最大 PPP\\texttt{PPP}PPP 人数。

    题目分析

    核心问题

    本题可以抽象为一个图论+动态规划(背包) 问题:

  • 图模型 :议员构成一个二分图,左侧是 DSP\\texttt{DSP}DSP 议员(编号 1..D1..D1..D ),右侧是 PPP\\texttt{PPP}PPP 议员(编号 D+1..D+PD+1..D+PD+1..D+P )。敌对关系是连接左右两侧的边。
  • 约束条件 :敌对议员最终必须属于不同政党。
  • 操作 :可以支付一定价格让某个议员改变政党(从 DSP\\texttt{DSP}DSP 变为 PPP\\texttt{PPP}PPP 或反之)。
  • 目标 :在不超过预算 BBB 的前提下,最大化最终属于某个政党的人数。
  • 关键观察

    由于敌对关系只存在于不同初始党派的议员之间,整个图由若干个连通分量组成。在每个连通分量内部,一旦确定了某个议员的最终党派,由于敌对关系的传递性,整个分量的最终分配方案就确定了(二分图染色)。

    对于每个连通分量,我们有两种可能的分配方案:

    • 方案 AAA :让分量中的第一个节点保持其初始党派。
    • 方案 BBB :让分量中的第一个节点改变到对立党派。

    每种方案都有对应的:

    • 成本 :需要支付给那些初始党派与最终党派不同的议员的价格总和。
    • 收益 :最终属于 DSP\\texttt{DSP}DSP 的人数 ddd 和属于 PPP\\texttt{PPP}PPP 的人数 ppp

    问题转化

    这样,原问题就转化为一个分组背包问题:

    • 每个连通分量是一个“物品组”,有两种选择(方案 AAA 或方案 BBB)。
    • 背包容量是预算 BBB
    • 选择每个分量的方案,使得总成本不超过 BBB ,并最大化总收益( DSP\\texttt{DSP}DSP 人数或 PPP\\texttt{PPP}PPP 人数)。

    我们需要分别求解两个背包问题:一个最大化 DSP\\texttt{DSP}DSP 人数,另一个最大化 PPP\\texttt{PPP}PPP 人数。

    算法步骤

  • 建图与连通分量分解

    • DSP\\texttt{DSP}DSP 议员编号为 1..D1..D1..DPPP\\texttt{PPP}PPP 议员编号为 D+1..D+PD+1..D+PD+1..D+P
    • 根据敌对关系建立无向图。
    • 使用 DFS\\texttt{DFS}DFSBFS\\texttt{BFS}BFS 找出所有连通分量。
  • 对每个连通分量计算两种方案

    • 通过二分图染色确定分量内节点的颜色分配( 000 表示最终属于 DSP\\texttt{DSP}DSP111 表示最终属于 PPP\\texttt{PPP}PPP )。
    • 方案 AAA :假设第一个节点颜色为 000 (如果它是 DSP\\texttt{DSP}DSP )或 111 (如果它是 PPP\\texttt{PPP}PPP )。
    • 方案 BBB :将方案 AAA 的颜色取反。
    • 分别计算两种方案的成本、最终 DSP\\texttt{DSP}DSP 人数和 PPP\\texttt{PPP}PPP 人数。
  • 动态规划(背包)

    • 定义 dp[b]dp[b]dp[b] 表示使用预算 bbb 时能获得的最大人数。
    • 对于每个连通分量,从 BBB 向下遍历更新 dpdpdp
      • dp[b]=max⁡(dp[b],dp[b−costA]+gainA)dp[b] = \\max(dp[b], dp[b-cost_A] + gain_A)dp[b]=max(dp[b],dp[bcostA]+gainA)
      • dp[b]=max⁡(dp[b],dp[b−costB]+gainB)dp[b] = \\max(dp[b], dp[b-cost_B] + gain_B)dp[b]=max(dp[b],dp[bcostB]+gainB)
    • 分别计算最大化 DSP\\texttt{DSP}DSPPPP\\texttt{PPP}PPP 的情况。
  • 复杂度分析

    • 节点数 N=D+P≤200N = D + P \\leq 200N=D+P200 ,边数 R≤2000R \\leq 2000R2000
    • 连通分量分解: O(N+R)O(N + R)O(N+R)
    • 对每个分量染色: O(N+R)O(N + R)O(N+R)
    • 背包 DP : O(C×B)O(C \\times B)O(C×B) ,其中 CCC 是连通分量个数(最多 NNN ), B≤104B \\leq 10^4B104
    • 总复杂度在可接受范围内。

    代码实现

    // Exposing Corruption
    // UVa ID: 13008
    // Verdict: Accepted
    // Submission Date: 2026-01-23
    // UVa Run Time: 0.040s
    //
    // 版权所有(C)2026,邱秋。metaphysis # yeah dot net

    #include <bits/stdc++.h>
    using namespace std;

    const int MAXN = 210;
    const int MAXB = 10010;
    vector<int> graph[MAXN];
    int priceD[MAXN], priceP[MAXN], D, P, R, B;
    bool visited[MAXN];

    struct Comp {
    int c0, d0, p0, c1, d1, p1;
    };

    vector<Comp> comps;

    void dfs(int u, vector<int>& nodes) {
    visited[u] = true;
    nodes.push_back(u);
    for (int v : graph[u]) if (!visited[v]) dfs(v, nodes);
    }

    void build() {
    fill(visited, visited + MAXN, false);
    comps.clear();
    for (int i = 1; i <= D + P; ++i) if (!visited[i]) {
    vector<int> nodes;
    dfs(i, nodes);

    // BFS染色
    vector<int> color(nodes.size(), 1);
    color[0] = 0;
    queue<int> q; q.push(0);
    while (!q.empty()) {
    int idx = q.front(); q.pop();
    int u = nodes[idx];
    for (int v : graph[u]) {
    auto it = find(nodes.begin(), nodes.end(), v);
    if (it == nodes.end()) continue;
    int vIdx = it nodes.begin();
    if (color[vIdx] == 1) color[vIdx] = 1 color[idx], q.push(vIdx);
    }
    }

    // 计算两种方案
    Comp comp = {0, 0, 0, 0, 0, 0};
    for (int j = 0; j < nodes.size(); ++j) {
    int node = nodes[j], init = (node <= D) ? 0 : 1;
    int final0 = color[j], final1 = 1 final0;

    // 方案0
    if (init == 0) {
    if (final0 == 0) comp.d0++;
    else comp.p0++, comp.c0 += priceD[node];
    } else {
    if (final0 == 0) comp.d0++, comp.c0 += priceP[node];
    else comp.p0++;
    }

    // 方案1
    if (init == 0) {
    if (final1 == 0) comp.d1++;
    else comp.p1++, comp.c1 += priceD[node];
    } else {
    if (final1 == 0) comp.d1++, comp.c1 += priceP[node];
    else comp.p1++;
    }
    }
    comps.push_back(comp);
    }
    }

    int solve(bool forDSP) {
    vector<int> dp(B + 1, 0);
    for (const Comp& c : comps) for (int b = B; b >= 0; b) {
    if (b >= c.c0) dp[b] = max(dp[b], dp[b c.c0] + (forDSP ? c.d0 : c.p0));
    if (b >= c.c1) dp[b] = max(dp[b], dp[b c.c1] + (forDSP ? c.d1 : c.p1));
    }
    return dp[B];
    }

    int main() {
    while (cin >> D >> P >> R >> B) {
    for (int i = 0; i < MAXN; ++i) graph[i].clear();
    for (int i = 1; i <= D; ++i) cin >> priceD[i];
    for (int j = 1; j <= P; ++j) cin >> priceP[j + D];
    for (int i = 0; i < R; ++i) {
    int x, y; cin >> x >> y;
    graph[x].push_back(y + D);
    graph[y + D].push_back(x);
    }
    build();
    cout << solve(true) << " " << solve(false) << endl;
    }
    return 0;
    }

    总结

    本题是一道综合性较强的题目,需要结合图论(二分图染色)和动态规划(背包问题)进行求解。关键是将原问题分解为连通分量,并识别出每个分量只有两种可能的分配方案。通过将问题转化为分组背包,可以高效地求解最大人数。算法复杂度合理,能够处理题目给定的数据范围。

    赞(0)
    未经允许不得转载:171主机测评 » UVa 13008 Exposing Corruption
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址