题干:
有一种跳跃游戏:假设初始位置在数轴的原点处,每一次可以选择两种操作: 向前k步或向后一步(k为当前的移动次数,即第k次跳跃k步)。给定一个终点D(D>0),请问最少经过多少次跳跃能恰好到达终点。(你可以跳到数轴的负半轴上)。
输入格式:
输入一个整数D(1≤D≤1018),代表终点。
输出格式:
输出一个整数,代表恰好到达D的最少的跳跃步数。
输入样例1:
在这里给出一组输入。例如:
5
结尾无空行
输出样例1:
4
结尾无空行
hint: 0->1->3->6->5
输入样例2:
在这里给出一组输入。例如:
6
结尾无空行
输出样例2:
3
结尾无空行
hint: 0->1->3->6
解题报告:
对于这种构造的题目,先别想着通过证明得出一个策略。最好的思路是先想策略,或者说猜一个策略,然后去证明这个策略是最优的。
本题猜的策略就是一直向前跑,挑一步往后跑。
猜这个策略的原因:简单模拟下发现,如果一直往前跑,则肯定是最少的跳跃次数,但是有可能会跳过了。考虑能不能再多用一步跳回来。发现是可以的。
详细证明:如果都往前跳,则第i步的贡献是i,如果第x步(x>0)向后跳了,则贡献是-(x+1),所以我们只需要挑选合适的x,就能做到了。x=0要特判,即代码中sum-1=n的情况。
AC代码:
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
long long n;
int main()
{cin>>n;long long l = 1, r = 1e10, mid, ans, sum;while(l <= r) {mid = (l+r)/2;sum = mid*(mid+1)/2;if(sum >= n) {ans = mid;r = mid - 1;}else {l = mid + 1;}}sum = ans * (ans + 1) / 2;if(sum-1 == n) cout << ans+1 << endl;else cout << ans << endl;return 0;
}