UVa1104/LA5131 Chips Challenge

UVa1104/LA5131 Chips Challenge

题目链接

  本题是2011年icpc世界总决赛D

题意

  在一个N×N(N≤40)网格里放芯片。其中一些格子已经放了芯片(用C表示),有些格子不能放(用/表示),有些空格子可以放或者不放芯片(用.表示)。还要放一些芯片(用W表示),使得第i行的总芯片个数(C和W之后)等于第i列。为了保证散热,任意行/列的芯片(包括C和W)不能超过总芯片数的A/B。

分析

  先不考虑任意行/列的芯片不能超过总部件数的A/B ,则是上下界循环费用流问题:将行 i i i看成结点 x i x_i xi,列 j j j看成结点 y j y_j yj,已经放了芯片的格子 ( i , j ) (i,j) (i,j)对应上下界均为1费用为0的有向边 i → j i\rightarrow j i→j,可以放或者不放的格子 ( i , j ) (i,j) (i,j)对应容量为1费用为0的有向边 x i → y j x_i\rightarrow y_j xi→yj; y i y_i yi向 x i x_i xi连容量为 i n f inf inf费用为-1的有向边来满足第i行的总芯片个数等于第i列的约束。

  再来看任意行/列的芯片不能超过总部件数的A/B 这一点怎么处理,如果枚举总芯片数(即枚举最大流),需要跑 O ( n 2 ) O(n^2) O(n2)次费用流,会超时。可以枚举每行/列的最大芯片总数 k k k(即 y i y_i yi向 x i x_i xi连容量为 k k k费用为-1的有向边)求出最大费用 c c c(即最大芯片总数):若 c × A < k × B c\times A<k\times B c×A<k×B则不满足任意行/列的芯片不能超过总部件数的A/B,否则更新答案。

  注意:上下界网络拆边调整后跑最大流,附加源点 s s s的出边都满流时才可行;循环流消除负权边,也是加源点 s s s的出边都满流时才可行。因此要检验最大流是否为源点 s s s流出的总容量。

  利用补集 的思想,可以得到更优的建图方法:已经放了芯片的格子不连边;可以放或者不放的格子 ( i , j ) (i,j) (i,j)连容量为1费用为1的有向边 x i → y j x_i\rightarrow y_j xi→yj(跑完费用流后此边若有流量则表示该格子不放芯片); x i x_i xi向 y i y_i yi连容量为 k k k费用为0的有向边来满足第i行的总芯片个数等于第i列 的约束;源点 s s s向 x i x_i xi连容量为第 i i i行所有已经放了芯片的格子总数与可以放或者不放的格子总数之和费用为0的有向边; y i y_i yi向汇点 t t t连容量为第 i i i列所有已经放了芯片的格子总数与可以放或者不放的格子总数之和费用为0的有向边。跑最小费用最大流,得到不能放的芯片数最小值(即最大芯片数的)。

AC 代码

上下界循环费用流版本

cpp 复制代码
#include <iostream>
#include <cstring>
using namespace std;

#define M 3520
#define N 82
struct edge {int u, v, cap, flow, cost;} e[M];
int g[N][N], q[M*N], a[N], d[N], p[N], cnt[N], hu[N], c, n, h, b, kase = 0; bool vis[N]; 

void add_edge(int u, int v, int cap, int cc) {
    e[c] = {u, v, cap, 0, cc}; g[u][cnt[u]++] = c++; e[c] = {v, u, 0, 0, -cc}; g[v][cnt[v]++] = c++;
}

int solve() {
    int s = 0, t = 2*n+1, f = 0, cc = 0;
    memset(cnt, c = 0, sizeof(cnt)); memset(a, 0, sizeof(a)); memset(d, 0, sizeof(d));
    for (int i=1; i<=n; ++i) for (int j=1; j<=n; ++j) {
        char x; cin >> x;
        if (x == 'C') ++a[j+n], ++a[i], ++f, ++d[i], --d[j];
        else if (x == '.') add_edge(i, j+n, 1, 0);
    }
    for (int i=1; i<=n; ++i) {
        hu[i] = c; add_edge(i, i+n, n, 1); add_edge(s, i, n, 0); add_edge(i+n, t, n, 0);
        if (a[i]) add_edge(i, t, a[i], 0);
        if (a[i+n]) add_edge(s, i+n, a[i+n], 0);
        if (d[i] || f*h < a[i]*b) cc = -1;
    }
    for (int k=n; k>0; --k) {
        for (int i=0; i<c; ++i) e[i].flow = 0;
        for (int i=1; i<=n; ++i) e[hu[i]].cap = e[hu[i]+2].cap = e[hu[i]+4].cap = k;
        int cost = n*k, flow = cost+f;
        while (true) {
            memset(d, 1, sizeof(d)); memset(vis, 0, sizeof(vis)); d[s] = 0; q[0] = s; a[s] = M;
            int head = 0, tail = 1;
            while (head < tail) {
                int u = q[head++]; vis[u] = false;
                for (int i=0; i<cnt[u]; ++i) {
                    const edge& ee = e[g[u][i]];
                    if (ee.cap > ee.flow && d[ee.v] > d[u]+ee.cost) {
                        d[ee.v] = d[u]+ee.cost;
                        p[ee.v] = g[u][i];
                        a[ee.v] = min(a[u], ee.cap-ee.flow);
                        if (!vis[ee.v]) vis[q[tail++] = ee.v] = true;
                    }
                }
            }
            if (d[t] > M) break;
            flow -= a[t]; cost -= d[t] * a[t];
            for (int u=t; u!=s; u=e[p[u]].u) e[p[u]].flow += a[t], e[p[u]^1].flow -= a[t];
        }
        if (flow || cost*h < k*b) continue;
        cc = max(cc, cost-f);
    }
    return cc;
}

int main() {
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    while (cin >> n >> h >> b && n) {
        int cc = solve();
        cout << "Case " << ++kase << ": ";
        cc < 0 ? cout << "impossible" << endl : cout << cc << endl;
    }
    return 0;
}

优化建图版本

cpp 复制代码
#include <iostream>
#include <cstring>
using namespace std;

#define M 3440
#define N 82
struct edge {int u, v, cap, flow, cost;} e[M];
int g[N][N], q[M*N], a[N], d[N], p[N], cnt[N], hu[N], c, n, h, b, kase = 0; bool vis[N]; 

void add_edge(int u, int v, int cap, int cc) {
    e[c] = {u, v, cap, 0, cc}; g[u][cnt[u]++] = c++; e[c] = {v, u, 0, 0, -cc}; g[v][cnt[v]++] = c++;
}

int solve() {
    int s = 0, t = 2*n+1, f = 0, c0 = 0, cc = -1;
    memset(cnt, c = 0, sizeof(cnt)); memset(a, 0, sizeof(a));
    for (int i=1; i<=n; ++i) for (int j=1; j<=n; ++j) {
        char x; cin >> x;
        if (x == 'C') ++a[j+n], ++a[i], ++f, ++c0;
        else if (x == '.') ++a[j+n], ++a[i], ++f, add_edge(i, j+n, 1, 1);
    }
    for (int i=1; i<=n; ++i) {
        hu[i] = c; add_edge(i, i+n, n, 0);
        if (a[i]) add_edge(s, i, a[i], 0);
        if (a[i+n]) add_edge(i+n, t, a[i+n], 0);
    }
    for (int k=0; k<=n; ++k) {
        for (int i=0; i<c; ++i) e[i].flow = 0;
        for (int i=1; i<=n; ++i) e[hu[i]].cap = k;
        int flow = f, cost = f;
        while (true) {
            memset(d, 1, sizeof(d)); memset(vis, 0, sizeof(vis)); d[s] = 0; q[0] = s; a[s] = M;
            int head = 0, tail = 1;
            while (head < tail) {
                int u = q[head++]; vis[u] = false;
                for (int i=0; i<cnt[u]; ++i) {
                    const edge& ee = e[g[u][i]];
                    if (ee.cap > ee.flow && d[ee.v] > d[u]+ee.cost) {
                        d[ee.v] = d[u]+ee.cost;
                        p[ee.v] = g[u][i];
                        a[ee.v] = min(a[u], ee.cap-ee.flow);
                        if (!vis[ee.v]) vis[q[tail++] = ee.v] = true;
                    }
                }
            }
            if (d[t] > M) break;
            flow -= a[t]; cost -= d[t] * a[t];
            for (int u=t; u!=s; u=e[p[u]].u) e[p[u]].flow += a[t], e[p[u]^1].flow -= a[t];
        }
        if (flow || cost*h < k*b) continue;
        cc = max(cc, cost - c0);
    }
    return cc;
}

int main() {
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    while (cin >> n >> h >> b && n) {
        int cc = solve();
        cout << "Case " << ++kase << ": ";
        cc < 0 ? cout << "impossible" << endl : cout << cc << endl;
    }
    return 0;
}
相关推荐
惆怅客1237 天前
UVa1507/LA5838 Shadow
计算几何·icpc·坐标变换·uva·二维凸包·离散化扫描法·圆和凸多边形的面积并
Estella-Ealine22 天前
5.10 周赛vp 2026 ICPC Gran Premio de Mexico 1ra Fecha E题
icpc·2026
所以遗憾是什么呢?23 天前
【题解】Codeforces Round 1097 (Div. 2, Based on Zhili Cup 2026) (致理杯) ABCDEF
数据结构·算法·acm·codeforces·icpc·ccpc·xcpc
漂流瓶jz1 个月前
UVA-1152 和为0的4个值 题解答案代码 算法竞赛入门经典第二版
数据结构·算法·二分查找·题解·aoapc·算法竞赛入门经典·uva
AKDreamer_HeXY1 个月前
QOJ 12255 - 36 Puzzle 题解
数据结构·c++·数学·算法·icpc·qoj
漂流瓶jz1 个月前
UVA-120 煎饼 题解答案代码 算法竞赛入门经典第二版
数据结构·c++·算法·排序·aoapc·算法竞赛入门经典·uva
所以遗憾是什么呢?2 个月前
【题解】Codeforces Round 1081 (Div. 2)
数据结构·c++·算法·acm·icpc·ccpc·xcpc
漂流瓶jz2 个月前
UVA-10384 推门游戏 题解答案代码 算法竞赛入门经典第二版
数据结构·算法·深度优先·题解·aoapc·算法竞赛入门经典·uva
漂流瓶jz2 个月前
UVA-11846 找座位 题解答案代码 算法竞赛入门经典第二版
数据结构·算法·排序算法·深度优先·aoapc·算法竞赛入门经典·uva
漂流瓶jz3 个月前
UVA-12569 树上的机器人规划(简单版) 题解答案代码 算法竞赛入门经典第二版
算法·图论·dfs·bfs·uva·算法竞赛入门经典第二版·11214