2019年秋季学期上机赛&练习赛小总结(自用)

mac2024-03-30  31

整除

题目描述: 已知四个整数A,B,C,D,请求出[A,B]中既不能被C整除也不能被D整除的整数的个数。 输入 多组输入,每组一行,分别为四个整数A,B,C,D,满足1≤A≤B≤1e18,1≤C,D≤1e9 输出 每组数据输出一行,为符合题意的整数个数

思路 由于1e18,逐个遍历会TLE,故使用容斥原理 [A,B]中能被c整除的整数个数为(b/c-(a-1)/c)个 辗转相除法求出cd的最大公约数mul 则最小公倍数为c*d/mul 则答案可以记为(b-a+1)-(n1+n2-n3) 代码(个人向)

#include <stdio.h> #include <stdlib.h> long long int lcm(long long int a,long long int b) { long long int m; while(b>0) {m=a%b;a=b;b=m;} return a; } int main() { long long int a,b,c,d; long long int x1,x2,x3,mul,ans; while (scanf("%lld%lld%lld%lld",&a,&b,&c,&d)!=EOF) { x1=b/c-(a-1)/c; x2=b/d-(a-1)/d; if(c<d) {mul=c;c=d;d=mul;} mul=(c*d)/lcm(c,d); x3=b/mul-(a-1)/mul; ans=b-a+1-(x1+x2-x3); printf("%lld\n",ans); } return 0; }

约瑟夫环

题目描述 有n个人围成一环顺时针报数,报到m及m的正整数倍的人出列,其余人继续,求最后剩下的一个人的编号 输入 两个正整数n,m( 1≤n,m≤3000) 输出 一个正整数编号

一般算法:使用数组

#include <stdio.h> #include <stdlib.h> int main() { int a[3005]={0}; int n,m,i,f=0,t=0,s=0; scanf("%d%d",&n,&m); do { ++t; if(t>n) t=1; if(a[t]==0) s++; if(s==m) { s=0; a[t]=1; f++; } }while(f!=n-1); for(i=1;i<=n;i++) {if(a[i]==0) printf("%d",i);} return 0; }

数学算法 每出列一人(k号),环上人从k+1开始重新编号,组成新的约瑟夫环。

k+1 k+2 k+3 … k-1 1-----2-----3-- … n-1

p重组后编号改变为p’,则有p=(p’+k)%n ∴有递推公式 f [ p ] = ( f [ p-1 ] + m )%p

#include <stdio.h> #include <stdlib.h> int main() { int n,m,i,s = 0; scanf("%d%d",&n,&m); for (i = 2; i <= n; i++) {s=(s+m)%i;} printf ("%d\n", s+1); return 0; }

倒序汉诺塔

题目描述 在一根柱子上从上往下按照大小顺序摞着n片黄金圆盘,把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在大圆盘上不能放小圆盘,在三根柱子之间一次只能移动一个圆盘。请输出全部的移动。 输入 一个整数n,表示汉诺塔的层数(n < 20) 输出 step #%d : move %d from %c to %c 思路参考和基础原码来自这里

递归问题的重要思考方式:只考虑一步的过程,上一步当作已知即可

#include <stdio.h> #include <stdlib.h> int ctr,w; void move(int id, char from, char to) {printf("step #%d: move %d from %c to %c\n",++ctr,w-id+1,from,to);} void hanoi(int n, char x, char y, char z) { if(n==0) return; hanoi(n-1,x,z,y);//把x上的n-1个盘子经由z移到y上 move(n,x,z);//把最下面的盘子移到z hanoi(n-1,y,x,z);//把y上n-1个盘子移到z上 } int main() { int n,ctr; scanf("%d",&n); w=n; hanoi(n,'A','B','C'); return 0; }

整数拆分

问题描述 提供一个正整数,试把它拆成若干个2的非负整数幂的和,并输出有多少种拆分方法。 输入 一个整数n( 0<n ≤ 200 ) 输出 一个整数,为拆分方法数

主要在于求出递推公式,然而我还是不会。 主要思路和递推公式来源于这里

注意一点,f(2m)=f(2m-1)+f(m)中 如果把2m-1写成2m,虽然理论上数值是一样的但是oj会REG,是因为f(2-2)没有定义。亲测如果增加判定f(2)=2就能过了。

补充一些我的思考。 易知f(2m+1)=f(2m),难点主要是f(2m)=f(2m-1)+f(m)的证明。 对于所有f(2m)的全拆分,将其分为包含1和不包含1的部分。其中包含1的部分肯定至少包含两个1,则该部分的数量和f(2m-2)是相等的(相当于各个分裂加上两个1);不含1的部分全部除2,则相当于f(m)的排列。

#include <stdio.h> #include <stdlib.h> int dev(int x) { int ans; if(x%2==1 && x!=1) ans=dev(x-1); if(x%2==0) ans=(dev(x-1)+dev(x/2)); if(x==1) ans= 1; return ans; } int main() { int x, ans; scanf("%d",&x); ans=dev(x); printf("%d",ans); return 0; }

求解非齐次线性方程组

问题描述 求解一方程个数和未知数个数相等的非齐次线性方程组,如果方程组有唯一解输出此解,否则输出字符串No Solution! 输入 一组数据,多行输入。 第一行一个整数 nn,代表方程个数和未知数个数。 接下来 nn 行每行 n+1n+1 个整数,代表每一个方程。 例如,对于第 i 行(首行是第11行)的 n+1 个整数分别是:ai1, ai2, ai3, …, ain,bi 其代表的方程是:ai1x1+ai2x2+ai3x3+ … +ainxn=bi 输出 一行,n个由单个空格隔开的两位小数(四舍五入)代表 x1,x2, … ,xn的唯一解。 如果没有唯一解则输出字符串No Solution!

高斯消元法的列主元消去法的参考

#include <stdio.h> #include <stdlib.h> #include <math.h> #include <string.h> #include <ctype.h> double a[110][110]; void print(double a[][110],int n) { int i,j; for(i=1;i<=n;i++) { for(j=1;j<=n+1;j++) printf("%.2lf ",a[i][j]); printf("\n"); } } void swap(double *a,double *b){double t;t=*a;*a=*b;*b=t;} void rref(double a[][110],int n) { int i,j,k,r,m;double temp; for(i=1;i<=n;i++) { r=i; for(j=i+1;j<=n;j++) { if(a[j][r]>a[i][r]) r=j; } if(r!=i) { for(k=i;k<=n+1;k++) swap(&a[r][k],&a[i][k]); } for(j=i+1;j<=n;j++) { temp=a[j][i]/a[i][i]; for(k=i;k<=n+1;k++) { a[j][k]-=a[i][k]*temp;} } } } int guess(double a[][110],int n) { int i,j; rref(a,n); for(i=n;i>=1;i--) { if(a[i][i]==0) return 1; else{ for(j=i+1;j<=n;j++) a[i][n+1]-=a[i][j]*a[j][n+1]; a[i][n+1]/=a[i][i];} } return 0; } int main() { int n,i,j,t; scanf("%d",&n);t=n; for(i=1;i<=n;i++) { for(j=1;j<=n+1;j++) {scanf("%lf",&a[i][j]);} } if(guess(a,n)) {printf("No Solution!");} else { for(i=1;i<=n;i++) printf("%.2lf ",a[i][n+1]); } return 0; }

表达式输出

问题描述 已知一个表达式,可以将小括号()内的内容匹配两遍。请输出表达式匹配的字符串,即将输入字符串小括号中的内容重复2遍,多层小括号作迭代处理。可结合输入输出样例理解。 输入 一行,一个字符串,即输入的表达式,保证所有字符ASCII码在[33,126]区间内。字符串长度不超过30。表达式的括号一定是前后匹配的。 输出 对于每组数据,输出一行,表达式匹配的字符串。 输入样例 123((0)(1)) 输出样例 12300110011

递归,对每个字符单独处理

#incude<stdio.h> int pr(int i){ int t; if(s[i]=='('){ t=pr(i+1);//寻找位置返回值 pr(i+1);//输出两遍 return pr(t);//从新位置开始递归 } else if(s[i]==')'){ return i+1;//返回位置 } else if(s[i]=='\0'){ return i;//返回位置 } else{ printf("%c",s[i]); return pr(i+1);//递归 } } int main() { fgets(s,50,stdin); pr(0); return 0; }
最新回复(0)