整除
题目描述:
已知四个整数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;
}