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

魔术球问题[网络流24题之4]

2016-05-19 18:01 288 查看

问题描述:

假设有 n 根柱子,现要按下述规则在这 n 根柱子中依次放入编号为 1,2,3,... 的球。

(1) 每次只能在某根柱子的最上面放球。

(2) 在同一根柱子中,任何 2 个相邻球的编号之和为完全平方数。

试设计一个算法,计算出在 n 根柱子上最多能放多少个球。例如,在 4 根柱子上最多可放 11 个球。

编程任务:

对于给定的 n ,计算在 n 根柱子上最多能放多少个球。

数据输入:

第 1 行有 1 个正整数 n ,表示柱子数。

结果输出:

n 根柱子上最多能放的球数

输入示例:

4

输出示例:

11

分析:

这个题目和最小路径覆盖问题差不多(几乎是一样的),对于每一个 x+y=i2(x<y),依照题意我们知道 x,y 都能且只能使用一次,所以我们可以把每一个数字 x 拆成 x1 和 x0,其中 x1 加上一个比 x1 小的数字构成一个完全平方数, x0 加上一个比 x0 大的数构成一个完全平方数,再把所有的 (y0,z1)(y0+z1=i2,y0<z1,i∈N+) 之间连一条有向边就是一个二分图匹配的问题了,下面给出构造图 N 的过程

1 :新增一个源 S 和汇 T

2 :把当前的整数 x 拆成 x0 和 x1

3 :连接一条容量为 1 ,从 S 到 x0 的有向边

4 :连接一条容量为 1 ,从 x1 到 T 的有向边

5 :连接一条容量为 1 ,从 x0 到 k(x0+k=i2,k∈N+,i∈N+) 的有向边

对于每一个数字执行完以上的 2−5 步之后如果不能在图 N 增加一点流量的话,就需要用一个新的柱子,当需要第 n+1 根柱子是,程序结束,当前数字减一就是最后的解

代码:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;

int tot;
int head[4747],nxt[47047],to[47047],wei[47047];
int c[4747];
int que[4747];
int n;
int num;
int now;

void add(int,int);
bool bfs();
bool dinic(int);

int main(){

freopen("ball.in","r",stdin);
freopen("ball.out","w",stdout);

scanf("%d",&n);

while(now <= n){

++num;
add(1,num<<1);
add((num<<1)+1,146);
for(int i=sqrt(num)+1;i*i<(num<<1);i++)
add((i*i-num)<<1,(num<<1)+1);
if(bfs())
dinic(1);
else
++now;

}

printf("%d",num-1);

return 0;

}

void add(int from,int tp){

++tot;nxt[tot]=head[from];head[from]=tot;to[tot]=tp;wei[tot]=1;
++tot;nxt[tot]=head[tp];head[tp]=tot;to[tot]=from;wei[tot]=0;

}

bool bfs(){

memset(c,0,sizeof c);
c[1] = 1;
que[1] = 1;

int H=0,T=1,now;

do{
now = que[++H];
for(int i=head[now];i;i=nxt[i])
if(!c[to[i]] && wei[i]){
c[to[i]] = c[now]+1;
que[++T] = to[i];
}
}while(H<T);

return c[146];

}

bool dinic(int place){

if(place == 146)
return true;

for(int i=head[place];i;i=nxt[i])
if(c[to[i]]==c[place]+1 && wei[i])
if(dinic(to[i])){
--wei[i];
++wei[i^1];
return true;
}

return false;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息