题目:
Description
Input
Output
Sample Input
0 0 0 0 0 0 0 100 5 20 34 325 4 5 6 7 283 102 23 320 203 301 203 40 -1 -1 -1 -1
Sample Output
Case 1: the next triple peak occurs in 21252 days. Case 2: the next triple peak occurs in 21152 days. Case 3: the next triple peak occurs in 19575 days. Case 4: the next triple peak occurs in 16994 days. Case 5: the next triple peak occurs in 8910 days. Case 6: the next triple peak occurs in 10789 days.思路:
这道题很简单。就是三个周期为23,28,33,在输入的第a天,第b天,第c天分别同时达到周期的顶峰,求下次同时达到顶峰的日子与d的相差天数。
需要注意的是,输入的a,b,c不一定为从这一年起的第一个周期!见题目我标红的字。
当时忽视了这句话,WA了好多次,哭哭啊!
当时错的测试用例:1 1 1 0, 答案应该为1,而我答案为21253。这个好解决,把i=0的情况加上去就好了。
后来想到,有一个测试用例我当时应该也错了:1 1 34 0,因为我每次都是从c开始加,而c不一定是这一年起第一个33的周期啊!
所以后来我用
while(c-33>0) c-=33;算出了c的第一个顶峰。
终于AC了。结果是125ms,好像还是不太快啊。
代码:
#include<iostream> using namespace std; int main(){ int a,b,c,d; int count=0; while(cin>>a){ if(a==-1) break; count++; cin>>b>>c>>d; bool isFind=false; int i=0; int newDay=0; while(isFind==false){ while(c-33>0) c-=33; newDay=c+i*33; if((newDay-b)%28==0&&(newDay-a)%23==0) isFind=true; i++; } if(newDay<=d) newDay+=21252; cout<<"Case "<<count<<": the next triple peak occurs in "<<(newDay-d)<<" days."<<endl; } return 0; }
后来去看别人的代码,他们用到了中国剩余定理,然后直接套公式就做出来,时间可以达到0ms。
看不懂中国剩余定理为啥公式是那样子,只好死记硬背了QAQ。
再看我们这道题,读入p,e,i,d 4个整数,求n。
已知(n+d)%23=p; (n+d)%28=e; (n+d)%33=i ,求n 。
两道题是一样的。但是韩信当时计算出结果的?
韩信用的就是“中国剩余定理”,《孙子算经》中早有计算方法,大家可以查阅相关资料。
“韩信点兵”问题计算如下:
因为n%3=2, n%5=3, n%7=2 且 3,5,7互质 (互质可以直接得到这三个数的最小公倍数)
令x= n%3=2 , y= n%5=3 ,z= n%7=2
使5×7×a被3除余1,有35×2=70,即a=2,用70;
使3×7×b被5除余1,用21×1=21,即b=1,用21;
使3×5×c被7除余1,用15×1=15,即c=1,用15。
那么n =(70×x+21×y+15×z)%lcm(3,5,7) = 23 这是n的最小解(Icm表示最小公倍数)
而韩信已知士兵人数在2300~2400之间,所以只需要n+i×lcm(3,5,7)就得到了2333,此时i=22
同样,这道题的解法就是:
已知(n+d)%23=p; (n+d)%28=e; (n+d)%33=i
使33×28×a被23除余1,用33×28×8=5544;
使23×33×b被28除余1,用23×33×19=14421;
使23×28×c被33除余1,用23×28×2=1288。
因此有(5544×p+14421×e+1288×i)% lcm(23,28,33) =n+d
又23、28、33互质,即lcm(23,28,33)= 21252;
所以有n=(5544×p+14421×e+1288×i-d)%21252
本题所求的是最小整数解,避免n为负,因此最后结果为n= [n+21252]% 21252
那么最终求解n的表达式就是:
n=(5544*p+14421*e+1288*i-d+21252)%21252;
当问题被转化为一条数学式子时,你会发现它无比简单。。。。直接输出结果了。
下面是代码:
#include<iostream> using namespace std; int main(void) { int p,e,i,d; int time=1; while(cin>>p>>e>>i>>d) { if(p==-1 && e==-1 && i==-1 && d==-1) break; int lcm=21252; // lcm(23,28,33) int n=(5544*p+14421*e+1288*i-d+21252)%21252; if(n==0) n=21252; cout<<"Case "<<time++<<": the next triple peak occurs in "<<n<<" days."<<endl; } return 0; }