「Luogu2765」[网络流24题] 魔术球问题 - 贪心
[网络流24题] 魔术球问题
时空限制:1000ms / 128MB
问题描述
假设有n根柱子,现要按下述规则在这n根柱子中依次放入编号为1,2,3,...的球。
(1)每次只能在某根柱子的最上面放球。
(2)在同一根柱子中,任何2个相邻球的编号之和为完全平方数。
试设计一个算法,计算出在n根柱子上最多能放多少个球。例如,在4 根柱子上最多可放11 个球。
编程任务
对于给定的n,计算在n根柱子上最多能放多少个球。
输入格式
第1 行有1个正整数n,表示柱子数。
输出格式
程序运行结束时,将n 根柱子上最多能放的球数以及相应的放置方案输出。文件的第一行是球数。接下来的n行,每行是一根柱子上的球的编号。
输入样例
4
输出样例
11
1 8
2 7 9
3 6 10
4 5 11
说明
4<=n<=55
分析
魔术球问题 网络流的经典例题
然而本蒟蒻并不会写网络流做法
所以今天我向大家介绍一种较为朴素的做法 贪心
本题的数据给的\(55\) 所以我们尽可以写O\(3\) O\(4\)算法
于是我便有了贪心的念头
查看本题题意不难发现 为了使柱子尽可能填满且尽可能多填数
可以把本题分为两种操作
(1)如果当前柱子有可以放置使前后成为平方数的位置 则放置
(2)如果当前没有可放置位置 开一个新柱子 将该球放到新柱子的第一项
这样我们就可以贪心辣~ 但是我们有几个地方需要注意
a要注意边界是柱子开到n个 如果不判边界会使程序进入死循环
b由于球号是自增的 所以在输出最大球号是要输出最后判断停止的球号-1
c在循环判断放球的时候可以找到位置立刻break 这样可以达到一个剪枝的效果
好辣 看到这里有没有觉得其实这个题很简单呢
我建议你们自己写写 如果还写不出来的话再看下面的完整代码
代码
#include<cstdio>
#include<cctype>
#include<cmath>
#define rg register
using namespace std;
const int N =56;
int f[N][10001],ball,book[N];//book来存每一个柱子有几个数
int n,cnt=1,spot;
inline int read(){
rg int f=0,x=0;
rg char ch=getchar();
while(!isdigit(ch)) f|=(ch=='-'),ch=getchar();
while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return f?-x:x;
}
inline bool jud(rg int x){//判断是否为平方数
rg int i=(int)sqrt(x);
if(i*i==x) return true;
else return false;
}
signed main(){
n=read();
while(cnt<=n){
ball++;
spot=0;
for(rg int i=1; i<=cnt; ++i){
if(jud(ball+f[i][book[i]])){//如果能找到
book[i]++;
f[i][book[i]]=ball;
break;
}
spot++;//每找一个循环都标记一下
}
if(spot==cnt){//走完所有循环都没找到
cnt++;
f[cnt][1]=ball;
book[cnt]=1;
}
}
printf("%d\n",ball-1);//一定要-1!!
for(rg int i=1; i<cnt; ++i){//i<cnt是因为现在的cnt是多出来的一个ball
for(rg int j=1; j<=book[i]; ++j)
printf("%d ",f[i][j]);
printf("\n");
}
return 0;
}
后话
当然网络流还有其它的题可以拿诸如此类的贪心做
既然分在了网络流大家最好还是要尝试一下网络流的做法
不要浪费每一道好题