T1 小苹果
【题目描述】
小Y的桌子上放着n个苹果从左到右排成一列,编号为从1到n。
小苞是小Y的好朋友,每天她都会从中拿走一些苹果。 每天在拿的时候,小苞都是从左侧第1个苹果开始、每隔2个苹果拿走1个苹果。 随后小苞会将剩下的苹果按原先的顺序重新排成一列。
小苞想知道,多少天能拿完所有的苹果,而编号为n的苹果是在第几天被拿走的?
【输出格式】
输出的第一行包含一个正整数n,表示苹果的总数。
【输出格式】
输出一行包含两个正整数,两个整数之间由一个空格隔开,分别表示小苞拿走所有
苹果所需的天数以及拿走编号为n的苹果是在第几天。
题目分析
因为题目要求苹果数量有 10^9 个,所以模拟法效率会很低,我们不能建立一个大数组或者链表,去模拟题目中的顺序一个一个取苹果。也不能使用递归,栈可能会爆掉。
1. 拿走苹果的总天数
我们先分析多少天可以拿走所有苹果
分析题目的关键点,观察苹果是如何取的:每隔两个取走一个
这句话提醒我们,如果每3个苹果分成一组,那么就是每组的第一个苹果会被拿走。
So,任何一个数都可以表示为被3 整除的部分,和余数部分,也就是:
n = 3 * p + r
其中, 3*p 是可以被三整除的部分,r 是余数部分,0 <= r <3
因此,取苹果的过程可以看作,从 p 个分组中,每次取一个,从余数部分中(如果有的话),每次取一个。
因此每次取苹果的个数为:
if (r > 0)
{
n -= p + 1; // 每个组拿一个,余数组拿一个
}
else
{
n -= p; // 每个组拿一个,没有余数组
}
需要注意的是,如果只有余数组了,每次只能拿一个苹果:
if (p > 0)
{
// 正常从所有组拿苹果
......
}
else
{
// 仅从余数组拿苹果,每次只能拿一个
n -= 1;
}
所以,完整的程序如下,使用 while 循环持续拿苹果,并记录天数即可:
#include <bits/stdc++.h>
typedef int intt;
#define int long long
using namespace std;
intt main()
{
int n = 0;
cin >> n;
int days = 0;
// n = 3p + r;
while (n > 0 )
{
int p = n / 3;
int r = n % 3;
if (p > 0)
{
if (r > 0)
{
// 每个组拿一个,余数组拿一个
n -= p + 1;
}
else {
// 每个组拿一个
n -= p;
}
}
else {
// 余数组一个一个拿
n -= 1;
}
days++;
}
cout << days;
return 0;
}
2. 第几天拿走最后一个苹果
考虑取苹果的过程,如果余数组苹果个数为 1, 那么这天就是取最后一个苹果的日子,
如果余数组苹果数量始终不为1, 那么最后一个苹果一定是最后一天拿走的。
因此,我们增加一个标志位,表示在取苹果的过程中,是否取走过最后一个,如果取走了,就记录天数;否则,就是最后一天。
if (p > 0)
{
if (r > 0)
{
......
if (!flag && r == 1)
{
lastDay = days + 1;
flag = true;
}
}
else
{
......
}
}
else
{
......
}
if (!flag)
{
lastday = days
}
完整代码如下:
#include <bits/stdc++.h>
typedef int intt;
#define int long long
using namespace std;
intt main()
{
int n = 0;
cin >> n;
int days = 0;
int lastDay = 0;
bool flag = false;
// n = 3p + r;
while (n > 0 )
{
int p = n / 3;
int r = n % 3;
if (p > 0)
{
if (r > 0)
{
// 每个组拿一个,余数组拿一个
n -= p + 1;
if (!flag && r == 1)
{
lastDay = days + 1;
flag = true;
}
}
else
{
// 每个组拿一个
n -= p;
}
}
else
{
// 余数组一个一个拿
n -= 1;
}
days++;
if (!flag)
{
lastDay = days;
}
}
cout << days << ' ' << lastDay;
return 0;
}
总结:
“任何一个整数,可以表示为被三整除的部分加上余数部分”:
n = 3 * p + r