1. 程式人生 > >uva 1104 chips challenge 費用流

uva 1104 chips challenge 費用流

題目大意:一個晶片可以看成由n*n的插槽組成,每個插槽有三種可能
1. 必須放置一個零件
2. 可以放一個零件也可以不放
3. 不能放置零件
要求
1. 第i行的零件要和第i列的零件一樣多
2. 第i行的零件數不能超過總零件數的A / B (A, B為給定的)
求 最多可以放的零件數 - 必須放置的零件數

首先看如何滿足第一個條件,假設知道了滿足該條件的答案為第i行零件數為xi,那麼把每行每列都看成一個點,
源點s向行i連容量為xi的邊,行i向列i連容量為inf的邊
列i向匯點t連容量為xi的邊,如果點(i,j)可以放零件,那麼連行i到列j容量為1的邊,如果該網路可以滿流,
那麼這個x即是答案,但是這是一個指數級別的演算法,考慮到
在上述的滿流網路中增加s->行i->列i->匯點這一通路的容量並不會影響原答案,因此我們可以直接將所有的xi設定為較大的值,
例如n,那麼只跑一次網路流就可以了。
不過這樣的網路對於所有的輸入都會滿流,如何讓他先流真正的邊呢,可以將真正的邊設定費用為1,
而額外增加的s->行i->列i->匯點這種假邊費用為0,那麼如果滿流且有費用不為0,
那麼即為合法答案,接著看如何滿足有的點必須放這一條件,最基本的想法可以搞成下界為1上界為1的邊,
不過也可以將必須放的點(i,j)的費用設為較大值L+1,即使所有費用為1的邊
全部選擇也不會大於L,這樣就可以保證先增廣必須放置的點所對應的邊,最後得到的總費用對L取模即為總零件數,
除以L則為必須放的零件數。最後看第二個條件,在xi的條件下已經得到了
零件總數f,而xi大於等於每一行的零件數,所以可以從高到低列舉xi,第一次滿足第二個條件的xi即為答案。
 

#include <cstdio>
#include <vector>
#include <queue>
#include <cstring>
using namespace std;

const int N = 100;
const int INF = 1e9;
const int F = 3000;

int map[N][N], C;
 
struct Edge {
    int from, to, cap, flow, cost;
    Edge(int u, int v, int ca, int f, int co) : from(u), to(v), cap(ca), flow(f), cost(co){};
};
 
struct MCMF {
    int n, m, s, t;
    vector<Edge> edges;
    vector<int> G[N];
    int inq[N];//是否在佇列中
    int d[N];//距離
    int p[N];//上一條弧
    int a[N];//可改進量
 
    void init(int n) { //初始化 
        this->n = n;
        for(int i = 0; i < n; i++)
            G[i].clear();
        edges.clear();
    }
 
    void addedge(int from, int to, int cap, int cost) { //加邊
        edges.push_back(Edge(from, to, cap, 0, cost));
        edges.push_back(Edge(to, from, 0, 0, -cost));
        int m = edges.size();
        G[from].push_back(m - 2);
        G[to].push_back(m - 1);
    }
 
    bool SPFA(int s, int t, int &flow, int &cost) {//尋找最小費用的增廣路,使用引用同時修改原flow,cost
        for(int i = 0; i < n; i++)
            d[i] = -INF;
        memset(inq, 0, sizeof(inq));
        d[s] = 0; inq[s] = 1; p[s] = 0; a[s] = INF;
        queue<int> Q;
        Q.push(s);
        while (!Q.empty()) {
            int u = Q.front();
            Q.pop();
            inq[u]--;
            for (int i = 0; i < 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]) {
                        inq[e.to]++;
                        Q.push(e.to);
                    }
                }
            }
        }
        if(d[t] == -INF) return false;//匯點不可達則退出
        flow += a[t];
        cost += d[t] * a[t];
        int u = t;
        while (u != s) { //更新正向邊和反向邊 
            edges[p[u]].flow += a[t];
            edges[p[u] ^ 1].flow -= a[t];
            u = edges[p[u]].from;
        }
        return true;
    }
 
    int MincotMaxflow(int s, int t) {
        int flow = 0, cost = 0;
        while (SPFA(s, t, flow, cost));
        return cost;
    }
};
 
int main()
{
    int n, a, b;
    int cas = 0;
    while(scanf("%d%d%d", &n, &a, &b) != EOF) {
        if (n == 0) break;
        cas ++;
        MCMF sol;
        int s = 0;
        int t = 2 * n + 1;
        char x[N];
        C = 0; 
        memset(map, 0, sizeof(map));
        for (int i = 0; i < n; i++) {
            scanf("%s", x);
            for (int j = 0; j < n; j++) {
                if (x[j] == '.') map[i + 1][j + 1] = 1;
                else if (x[j] == 'C') {
                    map[i + 1][j + 1] = 2;
                    C++;
                }
            }
        }
        for (int limit = n; limit >= 0; limit--) {
            sol.init(t + 1);
            for (int i = 1; i <= n; i++) {
                sol.addedge(s, i, limit, 0);
                sol.addedge(i + n, t, limit, 0);
                sol.addedge(i, i + n, INF, 0);
                for (int j = 1; j <= n; j++) {
                    if (map[i][j] == 1) sol.addedge(i, j + n, 1, 1);
                    if (map[i][j] == 2) sol.addedge(i, j + n, 1, F + 1);
                }
            }
            int f = sol.MincotMaxflow(s, t);
            if (f / F < C) {
                printf("Case %d: impossible\n", cas);
                break;
            }
            f %= F;
            if (f * a < b * limit) continue;
            else {
                printf("Case %d: %d\n", cas, f - C);
                break;
            }
        }
    }
    return 0;
}