Time limit: 3.000 seconds
限时3.000秒
Problem
问题
A computer programmer lives in a street with houses numbered consecutively (from 1) down one side of the street. Every evening she walks her dog by leaving her house and randomly turning left or right and walking to the end of the street and back. One night she adds up the street numbers of the houses she passes (excluding her own). The next time she walks the other way she repeats this and finds, to her astonishment, that the two sums are the same. Although this is determined in part by her house number and in part by the number of houses in the street, she nevertheless feels that this is a desirable property for her house to have and decides that all her subsequent houses should exhibit it.
有一个计算机程序员,她家所在的某条路上的住宅都在路的同一边,编号由1开始依次递增。她每天晚上都要出门溜狗,或向左走或向右走,直到街尾后返回。有一天溜狗时她计算了沿街的门牌号之和(不包括她家的),第二天她走了相反的方向也计算了门牌号之和,结果令她震惊——两次门牌号之和竟然相同。尽管这个结果是由沿街的房子数量以及她家的门牌号决定的,但她仍然觉得这非常奇妙,并决定将此作为以后选择住房的必要条件。
Write a program to find pairs of numbers that satisfy this condition. To start your list the first two pairs are: (house number, last number):
写一个程序来计算满足上述条件的参数。参数包含两个整数,第一个是她的房子的门牌号,第二个是最后一个房子门牌号。
6 8
35 49
Input and Output
输入与输出
There is no input for this program. Output will consist of 10 lines each containing a pair of numbers, each printed right justified in a field of width 10 (as shown above).
这个程序没有输入。输出前10个能满足上面条件的整数对,每对整数独占一行,每个整数都居右对齐,宽度为10个字符(示例如上)。
Analysis
分析
这道题可以用暴力搜索的方法来做,算法效率是O(n3),但事实上是可以用数论的方法来生成所有解的,下面将用最简单的数学语言进行介绍。设全部的房子数为n,她住的为k,那么按照题目的要求n和k必须满足:
- 1 + 2 + ... + (k – 1) = (k + 1) + (k + 2) + ... + (n) (1)
用等差数列求和公式Sn=n(a1+an)/2对(1)进行化简,得:
- (k – 1)[1 + (k – 1)] / 2 = (n – k)[(k + 1) + n] / 2, 两边同乘以2
- (k – 1)[1 + (k – 1)] = (n – k)[(k + 1) + n] (2)
将(2)式再次化简,得:
- 2k2 = n2 + n (3)
题目就转化为求出满足(3)式的最小的前10对正整数:n和k。先将上式两边配方,移项得:
- (2n + 1)2 – 2(2k)2 = 1 (4)
令:
- x = 2n + 1,y = 2k (5)
将(5)代入(4)式,可得:
- x2 – 2y2 = 1 (6)
不定方程(6)是一个典型的佩尔(Pell)方程,要推导求解该方程需要数论的一些基本概念,篇幅所限不再赘述,相关内容请参见维基百科:佩尔方程。为了方便程序实现,应使用递归公式进行求解。易知方程(6)的最小一组正整数解为(x0=3, y0=2),将题目所给的最小一组解(n=8,k=6)代入(5)式可得方程的解为(17, 12)。经验算可知(也可由下面的公式对(x0, y0)进行迭代求出),(17, 12)恰好为(6)式的第二组正整数解。至此初始值(前两组(x, y)解)计算完毕,后面的(x, y)可用迭代公式求出:
- xi+1 = xix0 + nyiy0
- yi+1 = xiy0 + yix0 (7)
用(7)迭代出前10组(x, y),然后用(5)式进行反算:
- n = (x – 1) / 2, k = y / 2 (8)
即可求得前10组n和k。算法实现中,注意保留前一次的x和y值。若在计算下一个y值前已完成x的迭代,那么y值的迭代将出现错误。此外由于本题没有输入,您也可以预先将答案生成,在程序中直接按格式输出结果,这样速度最快(不过有点无耻哈~)。
Solution
解答
#include <iomanip>
#include <iostream>
using namespace std;
int main(void) {
//循环10次,计算出每一组数据,x0和y0为最小解,x和y为迭代解
for (int i = 0, x0 = 3, y0 = 2, x = x0, y = y0, t; i < 10; ++i, x = t) {
t = x * x0 + 2 * y * y0; //用t临时保存算得的x
y = x * y0 + y * x0; //计算出y
//用x和y解出n和k并输出
cout << setw(10) << y / 2 << setw(10) << (t - 1) / 2 << endl;
}
return 0;
}