A - 利润
题目:
奶牛们开始了新的生意,它们的主人约翰想知道它们到底能做得多好。这笔生意已经做了
N(1≤N≤100,000) 天,每天奶牛们都会记录下这一天的利润 Pi(−1000≤Pi≤1000)。
约翰想要找到奶牛们在连续的时间期间(至少一天)所获得的最大的总利润,请你写一个计算最大利润的程序来帮助他。
Sample Input
7
-3
4
9
-2
-5
8
-3
Sample Output
14
思路:本题实质上可以看做求最大子数组问题
题解:
#include<bits/stdc++.h>
using namespace std;
int n,a[100005],f[100005];
int anss=-1000;
int main(void)
{
cin >> n;
for(int i=1;i<=n;i++)
{
cin >> a[i];
}
f[1]=a[1];
for(int i=2;i<=n;i++)
{
f[i]=max(f[i-1]+a[i],a[i]);//求出第i天的最大利润
anss=max(anss,f[i]);//表叫得出总共的最大利润
}
cout << anss ;
return 0;
}
B - 取石子游戏
题目:
1堆石子有n个,两人轮流取.先取者第1次可以取任意多个,但不能全部取完.以后每次取的石子数不能超过上次取子数的2倍。取完者胜.先取者负输出"Second win".先取者胜输出"First win".
Input
输入有多组.每组第1行是2<=n<2^31. n=0退出.
Output
先取者负输出"Second win". 先取者胜输出"First win".
参看Sample Output.
Sample Input
2
13
10000
0
Sample Output
Second win
Second win
First win
思路:
本题是“斐波那契博弈问题”,由于第一个人可以取任意数但不能取完,且第二个人可取的范围是 1~(第一个人取数的二倍)
PS:只要n是斐波那契数列中的数,那么先手必败
证明如下:
*将石子数量n记为f[i],
数学归纳法:
(1)当 i=2 时,先手的人只能取一个,那么先手必败,结论成立
(2)假设当 i<=k 时结论成立
则当i=k+1时,f[i]=f[k]+f[k-1],相当于我们将这堆石子看成两堆:k堆和k-1堆**
(之所以一定可以看成两堆,是因为假如先手第一次取的石子>=f[k-1],则后手可以直接取完获胜,因为 f[k]<2f]k-1])**
(3)对于k-1堆,无论先手怎么取,后手总能取到最后一个。设后手最后取到的石子数为x
如果先手第一次取的石子y>=f[k-1]/3, 则这堆石子剩余石子小于2y,就说明后手能一次取完剩余石子,此时x=f[k-1]-y,且x<=(2/3)f[k-1]
(4)由于接下来要取k堆中的石子了,所以比较一下 (2/3)f[k-1] 和 (1/2)f[k] 的大小,即比较 4f[k-1] 和 3f[k]的大小
3f[k] - 4f[k-1]=3f[k-2]-f[k-1]>0,即(1/2)f[k] 较大,
即 x < (1/2)*f[k],也就是说后手取完k-1堆后,先手不能一次取完k堆,所以游戏规则没有改变,同理得后手仍然能取到k堆的最后一课,所以结论成立。
以上是当n属于斐波那契数列 时,先手必败
当n不属于斐波那契数列时,根据“Zeckendorf定理”(查阅资料)得:
“任何正整数都可以表示为若干个不连续的斐波那契数之和”
题解:
#include<stdio.h>
int f[100];
int Fib(int n);
int main(void)
{
int n,k;
while(scanf("%d",&n))
{
if(n==0)
{
break;
}
k=Fib(n);
if(k)
{
printf("Second win\n");
}
else
{
printf("First win\n");
}
}
return 0;
}
int Fib(int n)
{
f[1]=1;
f[2]=1;
for(int i=3;i<=50;i++)
{
f[i]=f[i-1]+f[i-2];
if(f[i]==n)
return 1;
if(f[i]>n)
break;
}
return 0;
}