数论模板
[toc]
闲的无聊翻了一下快爆了的E盘,发现里面有不少“好东西“”,一堆关于数论的课件,所以就整理一下常见的数论模板。虽然我不会数论,但至少整理一下可以知道以后学什么(顺便水篇博客)
欧几里德:
#include<iostream> using namespace std; int hcf(int a,int b) { int r=0; while(b!=0) { r=a%b; a=b; b=r; } return(a); } lcd(int u,int v,int h) //u=a,v=b,h为最小公约数=hcf(a,b); { return(u*v/h); } int main() { int a,b,x,y; cin>>a>>b; x=hcf(a,b); y=lcd(a,b,x); cout<<x<<" "<<y<<endl; return 0; }
扩展欧几里德
#include <iostream> using namespace std; __int64 ext_euclid(__int64 a,__int64 b, __int64 &x, __int64 &y) { int t; __int64 d; if (b==0) {x=1;y=0;return a;} d=ext_euclid(b,a %b,x,y); t=x; x=y; y=t-a/b*y; return d; } void modular_equation(__int64 a,__int64 b,__int64 c)//ax = b(mod n) { __int64 d; __int64 x,y; d = ext_euclid(a,b,x,y); if ( c % d != 0 ) printf("No answer\n"); else { x = (x * c/d) % b ;// 第一次求出的x ; __int64 t = b/d; x = (x%t + t)%t; printf("%I64d\n",x);//最小的正数的值 for (int i=0;i<d;i++) printf("The %dth answer is : %ld\n",i+1,(x+i*(b/d))%b); //所有的正数值 } } /*函数返回值为gcd(a,b),并顺带解出ax+by=gcd(a,b)的一个解x,y, 对于不定方程ax+by=c的通解为: x=x*c/d+b/d*t y=y*c/d+a/d*t 当c%gcd(a,b)!=0时,不定方程无解.*/
中国剩余定理:
#include <iostream> using namespace std; int ext_euclid(int a,int b,int &x,int &y) //求gcd(a,b)=ax+by { int t,d; if (b==0) {x=1;y=0;return a;} d=ext_euclid(b,a %b,x,y); t=x; x=y; y=t-a/b*y; return d; } int China(int W[],int B[],int k) //W为按多少排列,B为剩余个数 W>B K为组数 { int i; int d,x,y,a=0,m,n=1; for (i = 0;i<k;i++) n *= W[i]; for (i=0;i<k;i++) { m=n/W[i]; d=ext_euclid(W[i],m,x,y); a=(a+y*m*B[i])%n; } if (a>0) return a; else return(a+n); } int main() { int B[100],W[100]; 求 int k ; a = 2( mod 5 ) cin >> k ; a = 3( mod 13) for(int i = 0 ; i < k ;i++) 的解 { 2 cin >> W[i]; 5 2 cin >> B[i]; 13 3 } 输出 42 cout<<China(W,B,k)<<endl; return 0; }
欧拉函数:
求小于n的所有欧拉数
#include <iostream> using namespace std; int phi[1000]; //数组中储存每个数的欧拉数 void genPhi(int n)//求出比n小的每一个数的欧拉数(n-1的) { int i, j, pNum = 0 ; memset(phi, 0, sizeof(phi)) ; phi[1] = 1 ; for(i = 2; i < n; i++) { if(!phi[i]) { for(j = i; j < n; j += i) { if(!phi[j]) phi[j] = j; phi[j] = phi[j] / i * (i - 1); } } } } 求n的欧拉数 int eular(int n) { int ret=1,i; for (i=2;i*i<=n;i++) if (n%i==0) { n/=i,ret*=i-1; while (n%i==0) n/=i,ret*=i; } if (n>1) ret*=n-1; return ret; //n的欧拉数 }
行列式计算:
#include <iostream> using namespace std; int js(int s[100][100],int n) { int z,j,k,r,total=0; int b[100][100]; /*b[N][N]用于存放,在矩阵s[N][N]中元素s[0]的余子式*/ if(n>2) { for(z=0;z<n;z++) { for(j=0;j<n-1;j++) for(k=0;k<n-1;k++) if(k>=z) b[j][k]=s[j+1][k+1]; else b[j][k]=s[j+1][k]; if(z%2==0) r=s[0][z]*js(b,n-1); /*递归调用*/ else r=(-1)*s[0][z]*js(b,n-1); total=total+r; } } else if(n==2) total=s[0][0]*s[1][1]-s[0][1]*s[1][0]; return total; }
排列:
long A(long n,long m) //n>m { long a=1; while(m!=0) {a*=n;n--;m--;} return a; }
组合:
long C(long n,long m) //n>m { long i,c=1; i=m; while(i!=0) {c*=n;n--;i--;} while(m!=0) {c/=m;m--;} return c; }
大数乘大数:
#include <iostream> #include <string> using namespace std; char a[1000],b[1000],s[10000]; void mult(char a[],char b[],char s[]) //a被乘数,b乘数,s为积 { int i,j,k=0,alen,blen,sum=0,res[65][65]={0},flag=0; char result[65]; alen=strlen(a);blen=strlen(b); for (i=0;i<alen;i++) for (j=0;j<blen;j++) res[i][j]=(a[i]-'0')*(b[j]-'0'); for (i=alen-1;i>=0;i--) { for (j=blen-1;j>=0;j--) sum=sum+res[i+blen-j-1][j]; result[k]=sum%10; k=k+1; sum=sum/10; } for (i=blen-2;i>=0;i--) { for (j=0;j<=i;j++) sum=sum+res[i-j][j]; result[k]=sum%10; k=k+1; sum=sum/10; } if (sum!=0) {result[k]=sum;k=k+1;} for (i=0;i<k;i++) result[i]+='0'; for (i=k-1;i>=0;i--) s[i]=result[k-1-i]; s[k]='\0'; while(1) { if (strlen(s)!=strlen(a)&&s[0]=='0') strcpy(s,s+1); else break; } } int main() { cin>>a>>b; mult(a,b,s); cout<<s<<endl; return 0;
}
大数乘小数:
#include <iostream> using namespace std; char a[100],t[1000]; void mult(char c[],int m,char t[]) // c为大数,m<=10,t为积 { int i,l,k,flag,add=0; char s[100]; l=strlen(c); for (i=0;i<l;i++) s[l-i-1]=c[i]-'0'; for (i=0;i<l;i++) { k=s[i]*m+add; if (k>=10) { s[i]=k%10;add=k/10;flag=1; } else { s[i]=k;flag=0;add=0; } } if (flag) { l=i+1;s[i]=add; } else l=i; for (i=0;i<l;i++) t[l-1-i]=s[i]+'0'; t[l]='\0'; } int main() { int i; cin>>a>>i; mult(a,i,t); cout<<t<<endl; return 0; }
大数加法:
#include <iostream> #include <string> using namespace std; char a[1000],b[1000],s[10000]; void add(char a[],char b[],char s[])//a被加数,b加数,s和 { int i,j,k,up,x,y,z,l; char *c; if (strlen(a)>strlen(b)) l=strlen(a)+2; else l=strlen(b)+2; c=(char *) malloc(l*sizeof(char)); i=strlen(a)-1; j=strlen(b)-1; k=0;up=0; while(i>=0||j>=0) { if(i<0) x='0'; else x=a[i]; if(j<0) y='0'; else y=b[j]; z=x-'0'+y-'0'; if(up) z+=1; if(z>9) {up=1;z%=10;} else up=0; c[k++]=z+'0'; i--;j--; } if(up) c[k++]='1'; i=0; c[k]='\0'; for(k-=1;k>=0;k--) s[i++]=c[k]; s[i]='\0'; } int main() { cin>>a>>b; add(a,b,s); cout<<s<<endl; return 0; }
大数减法:
#include <iostream> using namespace std; char a[1000],b[1000],s[10000]; void sub(char a[],char b[],char s[]) { int i,l2,l1,k; l2=strlen(b);l1=strlen(a); s[l1]='\0';l1--; for (i=l2-1;i>=0;i--,l1--) { if (a[l1]-b[i]>=0) s[l1]=a[l1]-b[i]+'0'; else { s[l1]=10+a[l1]-b[i]+'0'; a[l1-1]=b[l1-1]-1; } } k=l1; while(a[k]<0) {a[k]+=10;a[k-1]-=1;k--;} while(l1>=0) {s[l1]=a[l1];l1--;} loop: if (s[0]=='0') { l1=strlen(a); for (i=0;i<l1-1;i++) s[i]=s[i+1]; s[l1-1]='\0'; goto loop; } if (strlen(s)==0) {s[0]='0';s[1]='\0';} } int main() { cin>>a>>b; sub(a,b,s); cout<<s<<endl; return 0; }
大数阶乘:
#include <iostream> #include <cmath> using namespace std; long a[10000]; int factorial(int n) //n为所求阶乘的n!的n { int i,j,c,m=0,w; a[0]=1; for(i=1;i<=n;i++) { c=0; for(j=0;j<=m;j++) { a[j]=a[j]*i+c; c=a[j]/10000; a[j]=a[j]%10000; } if(c>0) {m++;a[m]=c;} } w=m*4+log10(a[m])+1; printf("%ld",a[m]); // 输出 for(i=m-1;i>=0;i--) // printf("%4.4ld",a[i]);// printf("\n"); return w; //返回值为阶乘的位数 } //储存方法很巧,每一个a[i]中存四位,不足四位在前加0补齐
大数求余:
int mod(int B) //A为大数,B为小数 { int i = 0,r = 0; while( A[i] != '\0' ) { r=(r*10+A[i++]-'0')%B; } return r ; //r为余数 }
高精度任意进制转换:
#include <iostream> #include <string> using namespace std; char s[1000],s2[1000]; // s[]:原进制数字,用字符串表示,s2[]:转换结果,用字符串表示 long d1,d2; // d1:原进制数,d2:需要转换到的进制数 void conversion(char s[],char s2[],long d1,long d2) { long i,j,t,num; char c; num=0; for (i=0;s[i]!='\0';i++) { if (s[i]<='9'&&s[i]>='0') t=s[i]-'0'; else t=s[i]-'A'+10; num=num*d1+t; } i=0; while(1) { t=num%d2; if (t<=9) s2[i]=t+'0'; else s2[i]=t+'A'-10; num/=d2; if (num==0) break; i++; } for (j=0;j<=i/2;j++) {c=s2[j];s2[j]=s2[i-j];s2[i-j]=c;} s2[i+1]='\0'; } int main() { while (1) { cin>>s>>d1>>d2; conversion(s,s2,d1,d2); cout<<s2<<endl; } return 0; }
判断一个数是否素数:
#include <iostream>//基本方法,n为所求数,返回1位素数,0为合数 #include <cmath> using namespace std; int comp(int n){ int i,flag=1; for (i=2;i<=sqrt(n);i++) if (n%i==0) {flag=0;break;} if (flag==1) return 1; else return 0;}
素数表:
int prime(int a[],int n) //小于n的素数 { int i,j,k,x,num,*b; n++; n/=2; b=(int *)malloc(sizeof(int)*(n+1)*2); a[0]=2;a[1]=3;num=2; for(i=1;i<=2*n;i++) b[i]=0; for(i=3;i<=n;i+=3) for(j=0;j<2;j++) { x=2*(i+j)-1; while(b[x]==0) { a[num++]=x; for(k=x;k<=2*n;k+=x) b[k]=1; } } return num; } //小于n的素数的个数} bool flag[1000000]; void prime(int M) //01表 { int i , j; int sq = sqrt(double(M)); for(i = 0 ;i < M ;i ++) flag[i] = true; flag[1] = false; flag[0] = false; for(i = 2 ;i <= sq ;i++) if(flag[i]) { for(j = i*i ;j < M ;j += i) flag[j] = false; } }
Miller_Rabin随机素数测试算法:
说明:这种算法可以快速地测试一个数是否 满足素数的必要条件,但不是充分条件。不 过也可以用它来测试素数,出错概率很小, 对于任意奇数n>2和正整数s,该算法出错概率 至多为2^(-s),因此,增大s可以减小出错概 率,一般取s=50就足够了。
#include<iostream> #include <cmath> using namespace std; int Witness(int a, int n) { int i, d = 1, x; for (i = ceil( log( (float) n - 1 ) / log(2.0) ) - 1; i >= 0; i--) { x = d; d = (d * d) % n; if ( (d == 1) && (x != 1) && (x != n-1) ) return 1; if ( ( (n - 1) & ( 1<<i ) ) >0 ) d = (d * a) % n; } return (d == 1 ? 0 : 1); } int Miller_Rabin(int n, int s) { int j, a; for (j = 0; j < s; j++) { a = rand() * (n - 2) / RAND_MAX + 1; if (Witness(a, n)) return 0; } return 1; } int main() { int x; cin>>x; cout<<Miller_Rabin(x , 50)<<endl; return 0; }
KMP:
#include<iostream> #include<string> using namespace std; char t[1010],p[100]; //t为大字符串,p为小字符串 int next[100]; void sn() { int j,k; next[0]=-1; j=1; do { k=next[j-1]; while(k!=-1 && p[k]!=p[j]) k=next[k]; next[j]=k+1; j+=1; } while(p[j]!='\0'); } int match(int x) //x是大字符串下标,从头开始为0,j为小字符串下标 { int k=x,j=0; if(t[0]=='\0') return 0; while(t[k]!='\0') { while(j!=-1 && p[j]!=t[k]) j=next[j]; k+=1; j+=1; if(p[j]=='\0') return k-j; //返回p所在的下标 } return -1; //搜索失败返回-1 } int main() { int x=0; sn(); int r=match( x ); cout<<r<<endl; }
(一)巴什博奕(Bash Game):
只有一堆n个物品,两个人轮流从这堆物品中取物,规定每次至少取一个,最多取m个。最后取光者得胜。
显然,如果n=m+1,那么由于一次最多只能取m个,所以,无论先取者拿走多少个,后取者都能够一次拿走剩余的物品,后者取胜。因此我们发现了如何取胜的法则:如果n=(m+1)r+s,(r为任意自然数,s≤m),那么先取者要拿走s个物品,如果后取者拿走k(≤m)个,那么先取者再拿走m+1-k个,结果剩下(m+1)(r-1)个,以后保持这样的取法,那么先取者肯定获胜。总之,要保持给对手留下(m+1)的倍数,就能最后获胜。
这个游戏还可以有一种变相的玩法:两个人轮流报数,每次至少报一个,最多报十个,谁能报到100者胜。
(二)威佐夫博奕(Wythoff Game):
有两堆各若干个物品,两个人轮流从某一堆或同时从两堆中取同样多的物品,规定每次至少取一个,多者不限,最后取光者得胜。
这种情况下是颇为复杂的。我们用(ak,bk)(ak ≤ bk ,k=0,1,2,...,n)表示两堆物品的数量并称其为局势,如果甲面对(0,0),那么甲已经输了,这种局势我们称为奇异局势。前几个奇异局势是:(0,0)、(1,2)、(3,5)、(4,7)、(6,10)、(8,13)、(9,15)、(11,18)、(12,20)。
可以看出,a0=b0=0,ak是未在前面出现过的最小自然数,而
bk= ak + k,奇异局势有如下三条性质:
1。任何自然数都包含在一个且仅有一个奇异局势中。
由于ak是未在前面出现过的最小自然数,所以有ak > ak-1 ,而 bk= ak + k >
ak-1 + k-1 = bk-1 > ak-1 。所以性质1。成立。
2。任意操作都可将奇异局势变为非奇异局势。
事实上,若只改变奇异局势(ak,bk)的某一个分量,那么另一个分量不可能在其他奇异局势中,所以必然是非奇异局势。如果使(ak,bk)的两个分量同时减少,则由于其差不变,且不可能是其他奇异局势的差,因此也是非奇异局势。
3。采用适当的方法,可以将非奇异局势变为奇异局势。
假设面对的局势是(a,b),若 b = a,则同时从两堆中取走 a 个物体,就变为了奇异局势(0,0);如果a = ak ,b > bk,那么,取走b
- bk个物体,即变为奇异局势;如果 a = ak , b <
bk ,则同时从两堆中拿走 ak - ab - ak个物体,变为奇异局势( ab - ak , ab - ak+ b - ak);如果a > ak ,b= ak + k,则从第一堆中拿走多余的数量a - ak 即可;如果a < ak ,b= ak + k,分两种情况,第一种,a=aj (j < k),从第二堆里面拿走 b - bj 即可;第二种,a=bj (j < k),从第二堆里面拿走 b - aj 即可。
从如上性质可知,两个人如果都采用正确操作,那么面对非奇异局势,先拿者必胜;反之,则后拿者取胜。
那么任给一个局势(a,b),怎样判断它是不是奇异局势呢?我们有如下公式:
ak =[k(1+√5)/2],bk=
ak + k (k=0,1,2,...,n
方括号表示取整函数)奇妙的是其中出现了黄金分割数(1+√5)/2 = 1。618...,因此,由ak,bk组成的矩形近似为黄金矩形,由于2/(1+√5)=(√5-1)/2,可以先求出j=[a(√5-1)/2],若a=[j(1+√5)/2],那么a
= aj,bj = aj + j,若不等于,那么a = aj+1,bj+1 = aj+1+ j + 1,若都不是,那么就不是奇异局势。然后再按照上述法则进行,一定会遇到奇异
局势。
(三)尼姆博奕(Nimm Game):
有三堆各若干个物品,两个人轮流从某一堆取任意多的物品,规定每次至少取一个,多者不限,最后取光者得胜。
这种情况最有意思,它与二进制有密切关系,我们用(a,b,c)表示某种局势,首先(0,0,0)显然是奇异局势,无论谁面对奇异局势,都必然失败。第二种奇异局势是(0,n,n),只要与对手拿走一样多的物品,最后都将导致(0,0,0)。仔细分析一下,(1,2,3)也是奇异局势,无论对手如何拿,接下来都可以变为(0,n,n)的情形。
计算机算法里面有一种叫做按位模2加,也叫做异或的运算,我们用符号(+)表示这种运算。这种运算和一般加法不同的一点是1+1=0。先看(1,2,3)的按位模2加的结果:
1 =二进制01
2 =二进制10
3 =二进制11 (+)
———————
0 =二进制00 (注意不进位)
对于奇异局势(0,n,n)也一样,结果也是0。
任何奇异局势(a,b,c)都有a(+)b(+)c =0。
如果我们面对的是一个非奇异局势(a,b,c),要如何变为奇异局势呢?假设 a < b< c,我们只要将 c 变为 a(+)b,即可,因为有如下的运算结果: a(+)b(+)(a(+)b)=(a(+)a)(+)(b(+)b)=0(+)0=0。要将c
变为a(+)b,只要从 c中减去 c-(a(+)b)即可。
例1。(14,21,39),14(+)21=27,39-27=12,所以从39中拿走12个物体即可达到奇异局势(14,21,27)。
例2。(55,81,121),55(+)81=102,121-102=19,所以从121中拿走19个物品就形成了奇异局势(55,81,102)。
例3。(29,45,58),29(+)45=48,58-48=10,从58中拿走10个,变为(29,45,48)。
例4。我们来实际进行一盘比赛看看:
甲:(7,8,9)->(1,8,9)奇异局势
乙:(1,8,9)->(1,8,4)
甲:(1,8,4)->(1,5,4)奇异局势
乙:(1,5,4)->(1,4,4)
甲:(1,4,4)->(0,4,4)奇异局势
乙:(0,4,4)->(0,4,2)
甲:(0.4,2)->(0,2,2)奇异局势
乙:(0,2,2)->(0,2,1)
甲:(0,2,1)->(0,1,1)奇异局势
乙:(0,1,1)->(0,1,0)
甲:(0,1,0)->(0,0,0)奇异局势
甲胜。
叉乘法求任意多边形面积
语法:result=polygonarea(Point *polygon,int N); |
||
参数: |
||
*polygon: |
多变形顶点数组 |
|
N: |
多边形顶点数目 |
|
返回值: |
多边形面积 |
|
注意: |
|
|
|
支持任意多边形,凹、凸皆可 |
|
|
多边形顶点输入时按顺时针顺序排列 |
|
源程序: |
|
|
typedef struct { double x,y; } Point; double polygonarea(Point *polygon,int N) { int i,j; double area = 0; for (i=0;i<N;i++) { j = (i + 1) % N; area += polygon[i].x * polygon[j].y; area -= polygon[i].y * polygon[j].x; } area /= 2; return(area < 0 ? -area : area); }
|
|
|
求三角形面积
语法:result=area3(float x1,float y1,float x2,float y2,float x3,float y3); |
|
参数: |
|
x1~3: |
三角形3个顶点x坐标 |
y1~3: |
三角形3个顶点y坐标 |
返回值: |
三角形面积 |
注意: |
|
|
需要 math.h |
源程序: |
|
|
float area3(float x1,float y1,float x2,float y2,float x3,float y3) { float a,b,c,p,s; a=sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)); b=sqrt((x1-x3)*(x1-x3)+(y1-y3)*(y1-y3)); c=sqrt((x3-x2)*(x3-x2)+(y3-y2)*(y3-y2)); p=(a+b+c)/2; s=sqrt(p*(p-a)*(p-b)*(p-c)); return s; }
|
两点距离(2D、3D)
语法:result=distance_2d(float x1,float x2,float y1,float y2); |
||
参数: |
||
x/y/z1~2: |
各点的x、y、z坐标 |
|
返回值: |
两点之间的距离 |
|
注意: |
|
|
|
需要 math.h |
|
源程序: |
|
|
float distance_2d(float x1,float x2,float y1,float y2) { return(sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2))); } float distance_3d(float x1,float x2,float y1,float y2,float z1,float z2) {return(sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)+(z1-z2)*(z1-z2))); }
|
||
射向法判断点是否在多边形内部
语法:result=insidepolygon(Point *polygon,int N,Point p); |
||
参数: |
||
*polygon: |
多边形顶点数组 |
|
N: |
多边形顶点个数 |
|
p: |
被判断点 |
|
返回值: |
0:点在多边形内部;1:点在多边形外部 |
|
注意: |
|
|
|
若p点在多边形顶点或者边上,返回值不确定,需另行判断 |
|
|
需要 math.h |
|
源程序: |
|
|
#define MIN(x,y) (x < y ? x : y) #define MAX(x,y) (x > y ? x : y) typedef struct { double x,y; } Point; int insidepolygon(Point *polygon,int N,Point p) { int counter = 0; int i; double xinters; Point p1,p2; p1 = polygon[0]; for (i=1;i<=N;i++) { p2 = polygon[i % N]; if (p.y > MIN(p1.y,p2.y)) { if (p.y <= MAX(p1.y,p2.y)) { if (p.x <= MAX(p1.x,p2.x)) { if (p1.y != p2.y) { xinters = (p.y-p1.y)*(p2.x-p1.x)/(p2.y-p1.y)+p1.x; if (p1.x == p2.x || p.x <= xinters) counter++;}}}} p1 = p2;} if (counter % 2 == 0) return(OUTSIDE); else return(INSIDE);}
|
判断点是否在线段上
语法:result=Pointonline(Point p1,Point p2,Point p); |
||
参数: |
||
p1、p2: |
线段的两个端点 |
|
p: |
被判断点 |
|
返回值: |
0:点在不在线段上;1:点在线段上 |
|
注意: |
|
|
|
若p线段端点上返回1 |
|
|
需要 math.h |
|
源程序: |
|
|
#define MIN(x,y) (x < y ? x : y) #define MAX(x,y) (x > y ? x : y) typedef struct { double x,y; } Point; int FC(double x1,double x2) { if (x1-x2<0.000002&&x1-x2>-0.000002) return 1; else return 0; } int Pointonline(Point p1,Point p2,Point p) { double x1,y1,x2,y2; x1=p.x-p1.x; x2=p2.x-p1.x; y1=p.y-p1.y; y2=p2.y-p1.y; if (FC(x1*y2-x2*y1,0)==0) return 0; if ((MIN(p1.x,p2.x)<=p.x&&p.x<=MAX(p1.x,p2.x))&& (MIN(p1.y,p2.y)<=p.y&&p.y<=MAX(p1.y,p2.y))) return 1; else return 0; }
|
|
|
判断两线段是否相交
语法:result=sectintersect(Point p1,Point p2,Point p3,Point p4); |
||
参数: |
||
p1~4: |
两条线段的四个端点 |
|
返回值: |
0:两线段不相交;1:两线段相交;2两线段首尾相接 |
|
注意: |
|
|
|
p1!=p2;p3!=p4; |
|
源程序: |
|
|
#define MIN(x,y) (x < y ? x : y) #define MAX(x,y) (x > y ? x : y) typedef struct { double x,y; } Point; int lineintersect(Point p1,Point p2,Point p3,Point p4) { Point tp1,tp2,tp3; if ((p1.x==p3.x&&p1.y==p3.y)||(p1.x==p4.x&&p1.y==p4.y)||(p2.x==p3.x&&p2.y==p3.y)||(p2.x==p4.x&&p2.y==p4.y)) return 2; //快速排斥试验 if ((MIN(p1.x,p2.x)<p3.x&&p3.x<MAX(p1.x,p2.x)&&MIN(p1.y,p2.y)<p3.y<MAX(p1.y,p2.y))|| (MIN(p1.x,p2.x)<p4.x&&p3.x<MAX(p1.x,p2.x)&&MIN(p1.y,p2.y)<p3.y<MAX(p1.y,p2.y))) ;else return 0; //跨立试验 tp1.x=p1.x-p3.x; tp1.y=p1.y-p3.y; tp2.x=p4.x-p3.x; tp2.y=p4.y-p3.y; tp3.x=p2.x-p3.x; tp3.y=p2.y-p3.y; if ((tp1.x*tp2.y-tp1.y*tp2.x)*(tp2.x*tp3.y-tp2.y*tp3.x)>=0) return 1; else return 0;}
|
||
点到线段最短距离
语法:result=mindistance(Point p1,Point p2,Point q); |
||
参数: |
||
p1、p2: |
线段的两个端点 |
|
q: |
判断点 |
|
返回值: |
点q到线段p1p2的距离 |
|
注意: |
|
|
|
需要 math.h |
|
源程序: |
|
|
#define MIN(x,y) (x < y ? x : y) #define MAX(x,y) (x > y ? x : y) typedef struct { double x,y; } Point; double mindistance(Point p1,Point p2,Point q) { int flag=1; double k; Point s; if (p1.x==p2.x) {s.x=p1.x;s.y=q.y;flag=0;} if (p1.y==p2.y) {s.x=q.x;s.y=p1.y;flag=0;} if (flag) { k=(p2.y-p1.y)/(p2.x-p1.x); s.x=(k*k*p1.x+k*(q.y-p1.y)+q.x)/(k*k+1); s.y=k*(s.x-p1.x)+p1.y; } if (MIN(p1.x,p2.x)<=s.x&&s.x<=MAX(p1.x,p2.x)) return sqrt((q.x-s.x)*(q.x-s.x)+(q.y-s.y)*(q.y-s.y)); else return MIN(sqrt((q.x-p1.x)*(q.x-p1.x)+(q.y-p1.y)*(q.y-p1.y)),sqrt((q.x-p2.x)*(q.x-p2.x)+(q.y-p2.y)*(q.y-p2.y))); }
|
求两直线的交点
语法:result=mindistance(Point p1,Point p2,Point q); |
||
参数: |
||
p1~p4: |
直线上不相同的两点 |
|
*p: |
通过指针返回结果 |
|
返回值: |
1:两直线相交;2:两直线平行 |
|
注意: |
|
|
|
如需要判断两线段交点,检验k和对应k1(注释中)的值是否在0~1之间,用在0~1之间的那个求交点 |
|
源程序: |
|
|
typedef struct { double x,y; } Point; int linecorss(Point p1,Point p2,Point p3,Point p4,Point *p) { double k; //同一直线 if ((p4.x-p3.x)*(p1.y-p3.y)-(p4.y-p3.y)*(p1.x-p3.x)==0&& (p2.x-p1.x)*(p1.y-p3.y)-(p2.y-p1.y)*(p1.x-p3.x)==0) return 2; //平行,不同一直线 if ((p4.y-p3.y)*(p2.x-p1.x)-(p4.x-p3.x)*(p2.y-p1.y)==0) return 0; k=((p4.x-p3.x)*(p1.y-p3.y)-(p4.y-p3.y)*(p1.x-p3.x))/((p4.y-p3.y)*(p2.x-p1.x)-(p4.x-p3.x)*(p2.y-p1.y)); //k1=((p2.x-p1.x)*(p1.y-p3.y)-(p2.y-p1.y)*(p1.x-p3.x))/((p4.y-p3.y)*(p2.x-p1.x)-(p4.x-p3.x)*(p2.y-p1.y)); (*p).x=p1.x+k*(p2.x-p1.x); (*p).y=p1.y+k*(p2.y-p1.y); return 1;//有交点}
|
||
整数拆分不可重复:
#include <iostream> #include <memory> using namespace std; const int MAX = 500; long long data[MAX][MAX]; int main() { int i,j; memset(data, 0, sizeof(int)*MAX); for(i = 0; i < MAX; i++) data[0][i] = 0; for(i = 0; i < MAX; i++) { for(j = 0; j < MAX; j++) { int sum = j*(j+1)/2; if(i > sum) data[i][j] = 0; else if(i == sum) data[i][j] = 1; else { if(i == j) data[i][j] = 1 + data[i][j-1]; else if(i < j) data[i][j] = data[i][i]; else data[i][j] = data[i-j][j-1] + data[i][j-1]; } } } int n; while(cin >> n) cout << data[n][n] << endl; return 0; }
整数拆分积最大:
nt data[100]; void main(int n;) { int k = 2; for(; n >= k; n-=k,k++) data[k] = k; for(int i = k-1; i >= 2 && n; i--, n--) data[i]++; data[k-1] += n; for(int j = 2; j < k; j++) cout << data[j] << " "; cout << endl; }
整数的无序拆分(可重复):
#include <iostream> //求出可分解个数 #include <memory> using namespace std; const int MAX = 600; long long data[MAX][MAX]; int main() { int i,j; memset(data, 0, sizeof(int)*MAX); for(j = 0; j < MAX; j++) data[0][j] = 0; for(i = 1; i < MAX; i++) { for(j = 1; j < MAX; j++) { if(i == j) data[i][j] = data[i][j-1]+1; else if(i < j) data[i][j] = data[i][i]; else data[i][j] = data[i][j-1]+data[i-j][j]; } } int n; while(cin >> n) cout << data[n][n] << endl; return 0; }
整数的无序拆分(可重复):
#include <iostream> //列出分解情况 #include <memory> using namespace std; const int MAX = 300; int data[MAX]; int main() { int i,n; cin >> n; for(i = 0; i < n; i++) { data[i] = 1; printf("1"); } printf("\n"); int size = n; while(size > 1) { int t, p, r; t = data[size-1] + data[size-2]; p = t / (data[size-2]+1); r = t % (data[size-2]+1); t = data[size-2]+1; i = size - 2; size = size - 2 + p; for(; i < size; i++) data[i] = t; data[size-1] += r; for(i = 0; i < size; i++) printf("%d", data[i]); printf("\n"); } return 0; }
约瑟夫环:
void f() { int n , k , m , i , j , start; while(cin>>n>>k>>m ) //n代表有多少个人 , k表示叫到k的人出列 , m 表示第一次谁先开始叫 { start = 0; if( !n && !k && !m) return 0; for(i = 1 ;i < n; i++) { start = (start + k) % i; } start++; start = (start + m) % n; if(!start) cout<<n<<endl; else cout<<start<<endl; } return ; } #include <stdio.h> main() { int n, m, i, s=0; printf ("N M = "); scanf("%d%d", &n, &m); for (i=2; i<=n; i++) s=(s+m)%i; printf ("The winner is %d\n", s+1); }
二分法:
#include<iostream> #include<Cmath> #define limit 1e-6 using namespace std; double x, y , c,p , a , b ; double f(double l) { return c*sqrt(x*x-l*l)+c*sqrt(y*y-l*l)-sqrt(x*x-l*l)*sqrt(y*y-l*l); } int main() { double mid; while(cin>>x>>y>>c) { a = 0; if(x > y) swap(x,y); b = x; //f(a) <= 0 , f(b) >= 0 mid = (a + b ) * 0.5000; while(1) { if(fabs(f(mid)) < limit) { printf("%.3f\n",mid); break; } if(f(mid)*f(a) <= 0) { b = mid; mid = 0.5000*(a + mid); } if(f(mid)*f(b) <= 0) { a = mid; mid = 0.5000*(mid + b); } } } return 0; }