魔术球问题
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题 04魔术球问题
- [网络流24题] 04 魔术球问题 (有向无环图最小路径覆盖, 最大流)
- 396. [网络流24题]魔术球问题(简化版
- cogs_396_魔术球问题_(最小路径覆盖+二分图匹配,网络流24题#4)
- 【多题合集】网络流24题练习(更新至魔术球问题)
- nefu486魔术球问题【有向无环图最小路径覆盖】
- 网络流24题之四 魔术球问题 最小路径覆盖
- cogs 396. [网络流24题]魔术球问题(简化版
- nefu486魔术球问题(增加节点最大流模板)
- PHP魔术引号所带来的安全问题分析
- PHP中overload __call魔术方法 和引用传递的问题
- kyeremal-网络流24题T4-魔术球问题
- [网络流24题]魔术球问题(简化版)
- 线性规划与网络流24题——04魔术球问题
- 网络流与线性规划24题04魔术球问题
- PHP魔术引号所带来的安全问题分析
- 魔术球问题 2011-12-29
- 魔术球问题[网络流24题之4]
- swust1739: 魔术球问题