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

魔术球问题

2017-03-16 11:18 330 查看

问题描述

假设有n根柱子,现要按下述规则在这n根柱子中依次放入编号为 1,2,3,...的球。
(1)每次只能在某根柱子的最上面放球。
(2)在同一根柱子中,任何2个相邻球的编号之和为完全平方数。
试设计一个算法,计算出在n根柱子上最多能放多少个球。例如,在4 根柱子上最多可放11个球。


编程任务

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


数据输入

由文件input.txt提供输入数据。文件第1 行有 1个正整数n,表示柱子数。


结果输出

程序运行结束时,将 n 根柱子上最多能放的球数以及相应的放置方案输出到文件output.txt中。文件的第一行是球数。接下来的n行,每行是一根柱子
上的球的编号。


输入文件示例

input.txt
4


输出文件示例

output.txt
11
1 8
2 7 9
3 6 10
4 5 11


题解

第一眼看到这个题,什么想法也没有,但是既然是网络流24题中的一题,那肯定可以用网络流解了,所以强行往网络流上靠。

xjb建图

感觉建的图很不错,非常优美,和解题报告比较一下?

【画面太美,不忍直视

好吧。重构思路,好像可以借鉴最小路径覆盖问题诶?

xjblg

诶?输出方案又错了,

关键时刻要有xp

xp提供的方案是:

正常跑dinic,在程序最后加一个用来判断是否输出的数组,从1开始循环,遇到一个没有被输出的数就输出,然后顺着这个点沿着流量已经用尽的边往下跑,直到没有可以到达的点为止。

见图:



首先我们发现1是没有被输出过的,于是沿着1的边往下跑,到达3,输出,发现3仍然可以跑到4,就输出4。在跑的过程中,不断给这些点打上输出标记。

又发现2没有被输出过,但是它的两条边的remain值都存在,也就是在这个最大流网络中,没有从2流出的流量,于是只能输出2。程序结束。

貌似还没有讲网络流的主体。

我们可以每次加入一个点,也就是增加一个小球,然后枚举每一个小于它的编号的数,如果可以加成完全平方数,我们就从这个较小的数连一条到达这个点的边,流量为1。当我们发现当前的点数减去最大流的值,也就是所需的柱子数已经比输入要求的大了(一定是大1,这对于理解输出很有帮助),停止加点。

枚举1到现在的点数减1,也就是能放的最多的小球数,进行上面说的输出操作。

有人可能会问:现在不是已经多一个点了么?这样输出不会将多加的那个点输出么?

其实是不会出现这种情况的。因为既然多加了这一个点,所需柱子数就加一,那么这个新的点一定是独自占了一个柱子,所以只要我们在输出的时候不枚举到它就可以了。

CODE:

#include<cstdio>
#include<cstring>
const int INF=1e9;
struct queue
{
int h,t;
int a[10001];
inline void clear(){h=1,t=0;}
inline void push(int n){a[++t]=n;}
inline int front(){return a[h];}
inline void pop(){h++;}
inline bool empty(){return h>t;}
}q;
struct edge
{
int next,to,remain;
}a[50000];
int head[5000];
int deep[5000];
int number[]={0,1,4,9,16,25,36,49,64,81,100,121,144,169,196,225,256,289,324,361,400,441,484,529,576,625,676,729,784,841,900,961,1024,1089,1156,1225,1296,1369,1444,1521,1600,1681,1764,1849,1936,2025,2116,2209,2304,2401,2500,2601,2704,2809,2916,3025,3136,3249,3364,3481,3600,3721,3844,3969,4096};
bool b[5000];
bool p[5000];
int n,g,ans,num=1,tmp,S,T;
inline int min(int a,int b){return a<b?a:b;}
inline void add(int x,int y,int cap)
{
a[++num].next=head[x],a[num].to=y,a[num].remain=cap,head[x]=num;
a[++num].next=head[y],a[num].to=x,head[y]=num;
}
inline bool bfs()
{
memset(deep,0x3f,sizeof(deep));
q.clear();q.push(S);
deep[S]=0;
while(!q.empty())
{
int tmp=q.front();q.pop();
for(int i=head[tmp];i;i=a[i].next)
if(deep[a[i].to]>INF&&a[i].remain)
q.push(a[i].to),deep[a[i].to]=deep[tmp]+1;
}
return deep[T]<INF;
}
int dfs(int now,int limit)
{
if(now==T||!limit) return limit;
int flow=0,f;
for(int i=head[now];i;i=a[i].next)
if(deep[a[i].to]==deep[now]+1&&a[i].remain&&(f=dfs(a[i].to,min(limit,a[i].remain))))
{
flow+=f,limit-=f,a[i].remain-=f,a[i^1].remain+=f;
if(!limit) return flow;
}
deep[now]=-1;
return flow;
}
inline int dinic()
{
int ans=0;
while(bfs()) ans+=dfs(S,INF);
return ans;
}
inline void print(int now)
{
printf("%d ",now);
p[now]=1;
int i=head[now];
while(i)
if(a[i].to!=S&&a[i].to!=T&&!a[i].remain)
{
printf("%d ",a[i].to-2000);
p[a[i].to-2000]=1;
i=head[a[i].to-2000];
}
else i=a[i].next;
printf("\n");
}
int main()
{
scanf("%d",&n);
S=4900;T=4901;
for(int i=1;i<=64;i++)
b[number[i]]=1;
while(1)
{
g++;
add(S,g,1);
add(g+2000,T,1);
for(int i=1;i<g;i++)
if(b[g+i]) add(i,g+2000,1);
tmp+=dinic();
if(g-tmp==n) ans=g;
else if(g-tmp==n+1)
{
printf("%d\n",ans);
for(int i=1;i<=g-1;i++)
if(!p[i]) print(i);
return 0;
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  网络流24题