網路流24題4. 魔術球問題
阿新 • • 發佈:2019-02-13
魔術球問題
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;
}