您的位置:首页 > 理论基础 > 计算机网络

网络流24题4. 魔术球问题

2017-03-02 16:16 246 查看

魔术球问题

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
, nxt[M];
int d
, vis
, q
, dfn;
int s, t;
int n, ans, tot;
bool is_sq
;
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: