ZOJ 1871: Steps
地址:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=871
题意:假设在一条直线上标记着数字刻度。每一步的步长不能为负数,且和前一个步长的关系必须是相等,或大于或小于1。第一步和最后一步的步长必须是1。则从 x 到 y 的最小步数是多少。
可以用数学语言重新描述题目如下:给出无符号整数 d ( = y-x ) ,可分解为满足下列条件的 n 个正整数的和,求 n 的最小值:
d = ∑ Si ; (i = 1 ... n)
且满足:
S1 = Sn = 1;
| S(i + 1) - Si | ≤ 1;
分析:根据题目要求,理想条件下,为了达到最小步数,步长变化应该是从1,2,3 ... 一直增加到最大值 M,然后逐渐减小到1:
distance = y - x ;
distance = 1 + 2 + 3 + ... + M + ... + 3 + 2 + 1 = M^2;
steps = 2 * M - 1; (步数)
因此如果距离正好是一个平方数(M 的平方),则步数一定是(2M-1),其中 M 为 最大步长。当距离不是平方数时,假设介于 M^2 和 (M+1)^2 之间,则
distance = M^2 + remain; (0 < remain <= 2M )
remain 是平方数后的尾数,如果remain 小于等于 M,则我们只需要把一个 remain 的步长插入到前面的序列中即可。如果 remain 大于 M,则一步是不够的,我们至少需要插入两步,M 和 remain - M(当然,也可以是 remain/2 和 remain - remain/2)。因为我们不能插入大于 M 的步长,否则最终累加的和会达到下一个平方数((M+1)的平方)。
因此根据上述分析,代码如下(代码中为了可读性,M 用 maxStep 表示):
#include <stdio.h> #include <math.h> int main(int argc, char* argv[]) { unsigned int x = 0, y = 0; unsigned int distance, maxStep, remain, steps; while(scanf("%lu %lu", &x, &y) != EOF) { distance = y - x; if(distance == 0) { printf("0\n"); continue; } maxStep = (unsigned int)sqrt(distance); remain = distance - maxStep * maxStep; steps = maxStep * 2 - 1; if(remain > 0) { if(remain <= maxStep) steps++; else steps += 2; } printf("%lu\n", steps); } return 0; }