1. 程式人生 > >網路流24題4. 魔術球問題

網路流24題4. 魔術球問題

魔術球問題

Description

假設有 n 根柱子,現要按下述規則在這 n 根柱子中依次放入編號為 1,2,3,…的球。
(1)每次只能在某根柱子的最上面放球。
(2)在同一根柱子中,任何 2 個相鄰球的編號之和為完全平方數。
試設計一個演算法,計算出在 n 根柱子上最多能放多少個球。例如,在 4 根柱子上最多可放 11 個球。
對於給定的 n,計算在 n 根柱子上最多能放多少個球。

Input

檔案第 1 行有 1 個正整數 n,表示柱子數。

Output

將 n 根柱子上最多能放的球數以及相應的放置方案輸出。第一行是球數。接下來的 n 行,每行是一根柱子上的球的編號。

題解

直接求解較為困難,考慮轉化為判定性問題,即在n根柱子上能不能放a個球。
a個球在柱子上從下到上必然是從小到大的,那麼兩個球如果能放在一起(和偉完全平方數)那麼就將他們之間連一條從小編號指向大編號的有向邊,如此一來,每根柱子可以看做是這個途中的一條路徑,而用最小路徑覆蓋就可以求出最少需要的柱子數量。那麼我們從小到大列舉a,一旦最小路徑覆蓋數大於n,那麼a-1就是答案。方案也只需找到有向圖對應的二分圖中的匹配即可。
在實際實現的過程中,從小到大列舉a,每次只需要在上次計算過的殘量網路中新增一些邊再繼續增廣即可,比二分答案然後每次重新建圖的時間複雜度小許多。

#include<cstdio>
#include<cmath> #include<iostream> #include<cstring> using namespace std; const int N = 5000 + 10, M = 500000 + 10, inf = 0x3f3f3f3f; struct Edge{ int fr, to, cap, flow; }edg[M]; int hd[N], nxt[M]; int d[N], vis[N], q[N], dfn; int s, t; int n, ans, tot; bool is_sq[N]; void insert(int u, int
v, int w){ edg[tot].fr = u, edg[tot].to = v, edg[tot].cap = w; nxt[tot] = hd[u], hd[u] = tot; tot++; edg[tot].fr = v, edg[tot].to = u; nxt[tot] = hd[v], hd[v] = tot; tot++; } bool bfs(){ int head = 1, tail = 1; q[1] = s; vis[s] = ++dfn; d[s] = 0; while(head <= tail){ int u = q[head++]; for(int i = hd[u]; i >= 0; i = nxt[i]){ Edge &e = edg[i]; if(vis[e.to] == dfn || e.cap <= e.flow) continue; vis[e.to] = dfn; d[e.to] = d[u] + 1; q[++tail] = e.to; } } return vis[t] == dfn; } int dfs(int x, int a){ if(x == t || a == 0) return a; int flow = 0, f; for(int i = hd[x]; i >= 0; i = nxt[i]){ Edge &e = edg[i]; if(d[e.to] == d[x] + 1 && (f = dfs(e.to, min(a, e.cap - e.flow))) > 0){ flow += f; e.flow += f; edg[i^1].flow -= f; a -= f; if(a == 0) break; } } return flow; } void work(){ scanf("%d", &n); memset(hd, -1, sizeof(hd)); s = 0; t = 1; for(int i = 1; i * i <= 5000; i++) is_sq[i*i] = 1; for(int a = 1, tmp = 1; ; a++, tmp++){ insert(s, a<<1, 1); insert(a<<1|1, t, 1); for(int i = 1; i < a; i++) if(is_sq[a+i]) insert(i<<1, a<<1|1, 1); while(bfs()) tmp -= dfs(s, inf); if(tmp > n){ printf("%d\n", a - 1); break; } } } int main(){ freopen("prog84.in", "r", stdin); freopen("prog84.out", "w", stdout); work(); return 0; }