题目描述:
n个水手来到一个岛上,采了一堆椰子后,因为疲劳都睡着了。一段时间后,第一个水手醒来,悄悄地将椰子等分成n份,多出一个椰子,便给了旁边的猴子,然后自己藏起一份,再将剩下的椰子重新合在一起,继续睡觉。不久,第二名水手醒来,同样将椰子了等分成n份,恰好也多出一个,也给了猴子。然而自己也藏起一份,再将剩下的椰子重新合在一起。以后每个水手都如此分了一次并都藏起一份,也恰好都把多出的一个给了猴子。第二天,n个水手醒来,发现椰子少了许多,心照不喧,便把剩下的椰子分成n份,恰好又多出一个,给了猴子。请问水手最初最少摘了多少个椰子?
输入:
多组测试数据,每组输入1个整数n(1 < n <= 9)
输出:
对于每组测试数据输出一行,值为最初摘的椰子数
数据样例:
5
样例输出:
15621
提示:多种可能性的情况下只求最小值
题目解析:
N个人分一堆椰子,每一个人单独分成N份并去掉一个,最后全部的人在一起将椰子分成N份并去掉一个。
分析:将椰子每次分N份并去掉一个,一共要进行这样的操作共N+1次(全部的人一起分也算是一次)
根据题目含义,我们假设y表示每一次每一个人分成的N份中的一份;
则存在一下关系:
这个人自己拿走一份,则一共留下N-1份,另这一次,这个人分的其中一份为y,那么就还剩下N-1份;在这N-1份之中,将继续进行分配所以可以得到以下的式子:
(n-1)*y(i)=n*y(i-1)+1 ( *k)
(注:y(i)和y(i-1)分别表示第i次和第i-1次醒来的那个水手进行分配后的一份。)
这个时候,将注意力放在提示上面:多种可能性的情况下只求最小值
什么时候我们能求得最小值呢?
如果是一个单调的函数,那么将一个增函数的自变量在定义域区间增大时,那么这个函数也在增大。
换句话说,我们只需要在这个增函数增长到第一次符合我们条件的时候,我们取得那个数便是我们需要得最小值。
那么我们就需要将(*k)进行相应的变换,让它符合我们“取得一个增函数”的想法。
对问题进行思考,我们能知道,如果我们知道了自己一共有多少个,那么经过一个f运算之后,能求得一个相对小的数,使之满足最后一次分配,仍然能分成N份还能多出一个,但是如果这样考虑,我们假设自己所知道的椰子总数是我们的自变量,那么它是逐渐减小的,而且没有办法赋予一个准确的初始值(如果有的话,你为什么还要写这一串代码呢?),所以我们假设,我们知道最后一次分出的个数(事实上,在(*k)那里,我已经那样操作了),那么这样的话,我们就把这个问题做了一次倒置,即我们已经知道了最后一次的椰子个数,我们所要做的唯一一件事情就是把所有人的椰子凑起来,看看最后能不能出现一个相对合理的数(相对合理,即每一次分配,大家都能得到整数个椰子,你也不想分到半个别人已经吃过的椰子吧?)。
这样以后我们能知道,我们的椰子数量将从1开始累加,知道最后(也就是我们用于计算的第一次)分配的椰子数,然后逐层返回,当我们返回了N+1次,并且每一次分配都相对合理的时候,我们即能得到返回了N+1次后,也就是没有分配时候的椰子的总数。
所以真正要解决的就是如何从一个很小的数做到逐层返回。
我们将(*k)进行变化,能得到它每一次上升的关系:
[y(i-1)*(n-1)+1]/n=y(i) (*L)
即通过y(i-1)我们可以得到它上一次分配时的一份y(i)
经过一番分析之后,相信你对自己要做,有了一个明确的概念:
1:输入一个n值,表示有多少人参与到了这一次的分配
2:通过(*L)式子实现逐层返回,如果返回了N+1次,并且都成功的话,bingo,你成功了
3:如果每一次返回的途中,有一次不成功的话,那么就让最后一次y加上1,再继续实验
#include<stdio.h>
int main()
{
double hand(int n);
int n;
while(scanf("%d",&n)!=EOF){
printf("%.0f\n",hand(n));
}
return 0;
}
double hand(int n)
{
double k,y;
double ans;
int i=1;
k=1.0;
y=k;
while(i<=n){
i++;
y=((n-1)*y-1)/n;
if(y!=(int)y){
k++;
y=k;
i=1;
}
}
ans=n*k+1;
return ans;
}
最后第N+1次返回单独列出的原因是:最后得总是要加上自己手上那一份!
当然你也可以返回N+1次后,再加上自己那一份:都是可以的。