uva 1104 chips challenge 費用流
阿新 • • 發佈:2019-01-08
題目大意:一個晶片可以看成由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; }