poj 1006

来源:互联网 发布:金山终端防护优化系统 编辑:程序博客网 时间:2024/06/06 04:43

题目概述:

某数除以23余a,除以28余b,除以33余c,这个数减去d后的数大于0且小于等于21252

输入:

每行a,b,c,d,多组数据之间没有空行,输入以一行-1 -1 -1 -1结束

限制:

0<=a,b,c,d<=365

输出:

每行一个字符串,若用%times%表示第%times%组(从1开始计数)数据,%num%表示这个数减去d后且在要求范围内的数,则字符串为

Case %times%: the next triple peak occurs in %num% days.

注意即便%num%==1,输出也是的也是days

样例输入:

0 0 0 0

0 0 0 100

5 20 34 325

4 5 6 7

283 102 23 320

203 301 203 40

24 29 34 0

24 29 34 1

24 29 34 2

-1 -1 -1 -1

样例输出:

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.

Case 7: the next triple peak occurs in 1 days.

Case 8: the next triple peak occurs in 21252 days.

Case 9: the next triple peak occurs in 21251 days.

讨论:

这个题里有大量的常数,本来可以写的更短,但是为了方便看就没写更短

中国剩余定理的应用,不得不说着实有些许理解上的难度,额到最后也没理解,不过倒是把实现都背过了(估计迟早要背错),对于三个数(不是三个呢?)的实现,流程大概是:

在三个除数中依次选一个,从另外两个除数的公倍数中找出可被这个除数除后余1的最小正数,用这个正数乘以选择的数对应的余数,完成三次后把三个结果加和,并对三个数的最小公倍数求模,这就是结果

用字母表示,有除数d1,d2,d3,及其最小公倍数lcm和余数r1,r2,r3,选择d1,令[(d2*d3)*n-1]/d1=m,化简后为(d2*d3)*n-d1*m=1,这里需要的正整数是(d2*d3)*n,之后得到(d2*d3)*n*r1,同理求出另外两个,加和得到sum,用abs(sum)%lcm就得到所求的数

题解状态:

164K,32MS,C++,991B

#include<cmath>#include<cctype>#include<cstring>#include<algorithm>#include<numeric>#include<vector>#include<set>#include<map>#include<queue>#include<list>#include<stack>using namespace std;#define lim 3//从中国剩余定理模版改过来的,这里就沿用了一下int rm[3];//remainder,记录三个余数const int dv[3] = { 23,28,33 };//divisor,作为除数的三个数const int lcm = 23 * 28 * 33;//least common multiple,拼写没错吧?这是三个数的最小公倍数,因为三个数互质,因此直接乘即可int exgcd(int a, int b, int &x, int &y)//近期用烂了的扩展欧几里德,之前因为背错int ans那行折腾了一阵子,这里只能算算法的一部分{if (!b) {x = 1, y = 0;return a;}int ans = exgcd(b, a%b, x, y);int t = x;x = y;y = t - a / b*y;return ans;}int fun(int a, int b, int c, int d){rm[0] = a, rm[1] = b, rm[2] = c;//只有三个数,就没用forint sum = 0;for (int p = 0; p < lim; p++) {int x, y;exgcd(lcm / dv[p], dv[p], x, y);//这里仅需要x,并且用lcm/dv[p]替代其余两个数的积,另外虽然公式里第二个参数应为-dv[p],但由于扩展欧几里德是在正数范围内解决问题,因此用正数即可sum += lcm / dv[p] * x*rm[p];}sum = sum%lcm - d;while (sum <= 0)sum += lcm;//参考讨论版的数据,找不到更好的方法控制范围,就用这个传统的办法了return sum;}int main(void){//freopen("vs_cin.txt", "r", stdin);int a, b, c, d,times=0;while (~scanf("%d%d%d%d", &a, &b, &c, &d) && a != -1 && b != -1 && c != -1 && d != -1) {//inputprintf("Case %d: the next triple peak occurs in %d days.\n", ++times,fun(a, b, c, d));//output//因为忘记输出有格式要求而wa了一次}}

EOF

0 0
原创粉丝点击