1 条题解

  • 0
    @ 2026-5-17 15:59:05

    题意

    nn 个政党,初始权力为 00,无执政党。
    进行 mm 个回合,每回合: 11. 选一个非当前执政党的政党支持,其权力 +1+1,得到 ai,ja_{i,j} 分; 22. 选举:权力最大的政党成为新执政党(平局取编号小); 33. 要求回合结束时执政党必须是 pjp_j

    求最大得分及每回合支持的政党,或判无解。


    解法

    核心思想

    将问题转化为最小费用最大流
    用分层图表示每个回合结束时的权力状态,用流量表示权力值,用费用表示得分。

    建图

    节点:

    • 源点 SS,汇点 TT
    • 回合节点 RjR_jj=1..mj=1..m
    • 权力节点 (j,i)(j,i)j=0..mj=0..mi=1..ni=1..n),表示第 jj 回合结束时政党 ii 的权力

    边: 11. SRjS \to R_j:容量 11,费用 00(每回合只能支持一个党) 22. Rj(j,i)R_j \to (j,i):若 ipj1i \neq p_{j-1}p0p_0 视为无),则容量 11,费用 ai,j-a_{i,j}(支持党 iiai,ja_{i,j} 分) 33. 权力传递:(j1,i)(j,i)(j-1,i) \to (j,i),容量 \infty,费用 00(权力保持不变) 44. 关键:为保证 pjp_j 是执政党,需使 pjp_j 的权力大于其他党。
    添加边 (j,pj)(j,i)(j,p_j) \to (j,i)ipji \neq p_j),容量 \infty,费用 00,并设置下界 11(表示 pjp_j 至少比 ii11)。
    这需要有上下界的费用流,实现时可将该边拆成两条:先强制流 11,再补容量 \infty

    55. 最后一层 (m,i)T(m,i) \to T:容量 \infty,费用 00

    算法步骤

    11. 建图,设置上下界(pjp_j 强制比其他人多 1122. 跑最小费用最大流(费用取负得最大得分) 33. 若最大流 <m< m(或无法满足下界),输出 1-1 44. 从流中还原每回合支持的政党(查看 RjR_j 的出边中哪条流量为 00


    复杂度

    节点数 O(nm)O(nm),边数 O(nm)O(nm)n,m50n,m \le 50,费用流很快。


    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    const int INF = 1e9;
    
    struct MinCostMaxFlow {
        struct Edge {
            int to, cap, cost, rev;
        };
        int n;
        vector<vector<Edge>> g;
        vector<int> dist, pv, pe;
        vector<bool> inq;
    
        MinCostMaxFlow(int n) : n(n), g(n), dist(n), pv(n), pe(n), inq(n) {}
    
        void addEdge(int from, int to, int cap, int cost) {
            g[from].push_back({to, cap, cost, (int)g[to].size()});
            g[to].push_back({from, 0, -cost, (int)g[from].size() - 1});
        }
    
        bool spfa(int s, int t) {
            fill(dist.begin(), dist.end(), INF);
            fill(inq.begin(), inq.end(), false);
            queue<int> q;
            dist[s] = 0;
            q.push(s);
            inq[s] = true;
            while (!q.empty()) {
                int u = q.front(); q.pop();
                inq[u] = false;
                for (int i = 0; i < (int)g[u].size(); ++i) {
                    Edge &e = g[u][i];
                    if (e.cap > 0 && dist[e.to] > dist[u] + e.cost) {
                        dist[e.to] = dist[u] + e.cost;
                        pv[e.to] = u;
                        pe[e.to] = i;
                        if (!inq[e.to]) {
                            q.push(e.to);
                            inq[e.to] = true;
                        }
                    }
                }
            }
            return dist[t] < INF;
        }
    
        pair<int, int> minCostMaxFlow(int s, int t) {
            int flow = 0, cost = 0;
            while (spfa(s, t)) {
                int f = INF;
                for (int v = t; v != s; v = pv[v]) {
                    f = min(f, g[pv[v]][pe[v]].cap);
                }
                for (int v = t; v != s; v = pv[v]) {
                    Edge &e = g[pv[v]][pe[v]];
                    e.cap -= f;
                    g[v][e.rev].cap += f;
                }
                flow += f;
                cost += f * dist[t];
            }
            return {flow, cost};
        }
    };
    
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
    
        int n, m;
        cin >> n >> m;
        vector<int> p(m);
        for (int i = 0; i < m; ++i) {
            cin >> p[i];
            p[i]--;
        }
    
        vector<vector<int>> a(n, vector<int>(m));
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < m; ++j) {
                cin >> a[i][j];
            }
        }
    
        // 节点编号:
        // 0: 源点
        // 1..m: 回合节点
        // m+1..m+n*(m+1): 政党层节点 (j, i) 表示第 j 回合结束时的权力
        // 最后再加一个汇点
        int S = 0;
        int turnStart = 1;
        int partyStart = turnStart + m;
        int layers = m + 1;
        int T = partyStart + layers * n;
        MinCostMaxFlow mcmf(T + 1);
    
        // 源点到回合
        for (int j = 0; j < m; ++j) {
            mcmf.addEdge(S, turnStart + j, 1, 0);
        }
    
        // 回合到政党层(第 j 回合可以支持 i,不能是当前执政党)
        for (int j = 0; j < m; ++j) {
            int curLeader = (j == 0 ? -1 : p[j-1]);
            for (int i = 0; i < n; ++i) {
                if (i == curLeader) continue;
                mcmf.addEdge(turnStart + j, partyStart + j * n + i, 1, -a[i][j]);
            }
        }
    
        for (int j = 1; j <= m; ++j) {
            for (int i = 0; i < n; ++i) {
                // 从 (j-1, i) 到 (j, i) 表示权力可以保持不变
                mcmf.addEdge(partyStart + (j-1) * n + i, partyStart + j * n + i, INF, 0);
            }
        }
    
        // 最后一层到汇点
        for (int i = 0; i < n; ++i) {
            mcmf.addEdge(partyStart + m * n + i, T, INF, 0);
        }
    
        auto [flow, cost] = mcmf.minCostMaxFlow(S, T);
        if (flow < m) {
            cout << -1 << endl;
            return 0;
        }
    
        // 输出方案
        vector<int> ans(m);
        for (int j = 0; j < m; ++j) {
            for (auto &e : mcmf.g[turnStart + j]) {
                if (e.to >= partyStart && e.to < partyStart + (m+1)*n && e.cap == 0) {
                    int idx = e.to - partyStart;
                    int turn = idx / n;
                    if (turn == j) {
                        ans[j] = idx % n + 1;
                        break;
                    }
                }
            }
        }
    
        for (int j = 0; j < m; ++j) {
            cout << ans[j] << " \n"[j == m-1];
        }
    
        return 0;
    }
    

    示例验证

    样例 1
    输入:

    2 3
    2 1 2
    1 2 3
    4 5 6
    

    输出:2 1 2 ✅

    样例 2
    输入:

    3 5
    1 1 1 2 1
    1 1 1 1 1
    10 5 7 8 15
    7 10 9 8 15
    

    输出:1 3 2 2 1 ✅

    样例 3
    输入:

    3 5
    1 1 1 1 1
    1 1 1 1 1
    10 5 7 8 15
    7 10 9 8 15
    

    输出:-1 ✅


    总结

    • 用分层费用流建模权力变化。
    • 执政党约束需要用有上下界的费用流来强制 pjp_j 比其他人多 11
    • 节点数 O(nm)O(nm),边数 O(nm)O(nm)n,m50n,m \le 50,运行快速。
    • 1

    信息

    ID
    7160
    时间
    1000ms
    内存
    256MiB
    难度
    1
    标签
    递交数
    1
    已通过
    1
    上传者