算法竞赛入门经典第十章例题与代码(排序从前到后)

7 篇文章 0 订阅

//给出一个这样的除法表达式:X1/X2/X3/…/Xk,其中Xi是正整数。除法表达式应当按照从左到右的顺序求和。

// 例如表达式/2/1/2的值为/4。但可以在表达式中嵌入括号以改变计算顺序,例如表达式(1/2)/(1/2)的值为。

//输入:X1,X2,X3,…,Xk,判断是否可以通过添加括号,使表达式的值为整数。k≤Xi≤

#include<iostream>

using namespace std;

int main()

{

__int64 n,m,t;

char c;

while(scanf("%I64d",&n)!=EOF)

{

      c=getchar();

   if(c!='/')

   {

    cout<<"YES"<<endl;

    continue;

   }

   scanf("%I64d",&t);

   while(c=getchar()=='/')

   {

    n=n%t;

    scanf("%I64d",&m);

    n*=m;

    

   }

   if(n%t==0)cout<<"YES"<<endl;

   else cout<<"NO"<<endl;

}

return 0;

}

//#include<stdio.h>

//#include<iostream>

//#include<algorithm>

//using namespace std;

//int main()

//{

//char s[3]={'1','2','3'};

//char *p=s;

//while(p!=NULL)

//{

//printf("%c\n",*(p++));

//}

//return 0;

//}

 

//10.2.4离散概率初步

//概率有一套很深的理论,很多和概率相关的问题并不需要特别的知识,熟悉排列组合就可以了。

//第个例子:连续抛次硬币,恰好有两次正面的概率有多大?用HT表示正面和背面,

//则一共有种可能的情况:HHHHHTHTHHTTTHHTHTTTHTTT。这种情况出现的可能性相同,

//概率各为/8{HHHHHTHTHHTTTHHTHTTTHTTT}称为样本空间(Sample Space)。

//所求的是恰好有两次正面这个事件(Event)的概率。这个事件可以表示为{HHTHTHTHH}

//它的概率是/8

//#include<iostream>

//using namespace std;

//double brithday(int year,int numpeople)

//{

//double ans=1.0;

//for(int i=0;i<numpeople;i++)

//ans=ans*(year-i)/year;

//return 1-ans;

//}

//int main()

//{

//cout<<brithday(365,23);

//return 0;

//}

 

.3.5 统计n-k特殊集的数目

如果由正整数构成的集合X满足以下条件,我们称它为n-k特殊集:

()集合X中的每个元素x均不超过n,即≤x≤n

()集合X中所有元素之和大于k

()集合X中不包含任意一对相邻的自然数。

给出nk,求n-k特殊集合有多少个。≤n≤≤k≤

样例输入:3

样例输出:

样例输入:55

样例输出:

样例输入:100

样例输出:

【分析】

n-k特殊集的个数为f(n,k),下面来建立它的递推式。由于集合中的元素不能重复,元素n要么在集合中恰好出现一次,要么不出现,二者不重复、不遗漏的把n-k特殊集分成了两部分:

()n不出现。除了规则()中的n需要修改为n-1外,其他均不变,因此有f(n-1,k)个。

()n出现一次。除了规则()中的n需要修改为n-2外(如果出现了n-1,则相邻自然数n-1n不满足条件,而规则()中的k应变为k-n

所以递推关系是:f(n,k)= f(n-1,k)+f(n-2,k-n)。接下来,来求边界。n≤时只有空集一个集合满足规则(),而此时所有元素和为,因此:

()当n≤k≥时,f(0,k)=0

()当n≤k<0时,f(0,k)=1

 在其他情况下,f均能按此递推式计算。但是有一个问题:如果用f[n][k]保存f(n,k)的值,nk需要允许负数。

//

//#include<iostream>

//#define F(i,j) (f[(i+1)][(j)+1])//用宏处理支持负数下标

//using namespace std;

//

//long long  f[200][500];//结果可能很大,需要高精度

//int main()

//{

//int i,j,n,k;

//cin>>n>>k;

//for(j=-1;j<=k;j++)

//F(-1,j)=F(0,j)=0;

//F(-1,-1)=F(0,-1)=1;

//for(i=1;i<=n;i++)

//{

//for(j=-1;j<=k;j++)//递推

//{

//F(i,j)=F(i-1,j);

//if(j-i<0)F(i,j)+=F(i-2,-1);

//else F(i,j)+=F(i-2,j-i);

//}

//}

//cout<<F(n,k)<<endl;

//return 0;

//}

//-1 大整数取模。

//输入正整数nm,输出n mod m的值。n≤,m≤

//【分析】首先,把大整数写成自左向右的形式:=((1*10+2)*10+3)*10+4

//然后用前面的公式(a+b)%m=(a%m+b%m)%m,每步取模,

//程序如下:  

//

//#include<cstdio>

//#include<iostream>

//#include<string>

//#include<cstdlib>

//using namespace std;

//int main()

// {

// char n[100];

// int m;

// scanf("%s%d",&n,&m);

// int len=strlen(n);

// int  ans=0;

// for(int i=0; i < len; i++)

//

// ans=(int)((long long)((ans*10)%m+n[i]%m)%m);

// /*ans = (int)(((long long)ans*10 + n[i])%m);*///也可以用这种方法取模,从最高位到最低位,每次取模了之后将余数乘以和下一个数相加继续取模,

//                                               //重复这个步骤,就好像做除法是一样的,只不过是分开了。

// printf("%d\n",ans);

// return 0;

// }

 

输入正整数amn,输出a^n mod m的值。a,n,m≤

分析:首先很容易想到ab%m=(a%m)(b%m)%m

可写出代码如下:

#include<iostream>

using namespace std;

int main()

{

int a,n=4,m=5;

/*cin>>a>>n>>m;*/

for(a=3;n<20;n++)

{

int ans=1;

for(int i=0;i<n;i++)

{

ans=(int)(long long)ans*a%m;  

}

cout<<"asn="<<ans<<endl;

}

return 0;

}

但是对于以上代码,当n比较大的时候就效率就变的很慢了,下面介绍分治法对效率进行提高

其实就是a^2i+1=a^2i*a.例:a^29=(a^14)^2*a,a^14=(a^7)^2,a^7=(a^3)^2*a,a^3=(a)^2*a.

所以这个地方只需要计算n此时的值是奇还是偶,然后决定是否乘以a,其他的直接在ans=a*a的基础上递归平方。即近似少算了/2的乘法运算

//#include<iostream>

//using namespace std;

//int pow_mod(int a,int n,int m)

//{

//if(n==0)return a;

//int x=pow_mod(a,n/2,m);

//long long ans=(long long)x*x%m;

//if(n%2==1)

//ans=ans*a%m;

//return (int)ans;

//}

//int main()

//{

//int a,n=4,m=5;

///*cin>>a>>n>>m;*/

//for(a=3;n<20;n++)

//{

//cout<<"asn="<<pow_mod(a,n,m)<<endl;

//}

//return 0;

//}

 

-4 无关的元素。

对于给定的n个数a1,a2,…,an,依次求出相邻两数之和,将得到一个新数列。

重复上述操作,最后结果将变成一个数。问这个数除以m的余数一定与哪些数无关?

例如n=3m=2时,第一次求和得到a1+a2a2+a3,再求和得到a1+2a2+a3,它除以的余数和a2无关。

1≤n≤≤m≤

【分析】

最后的求和式是a1,a2,…,an的线性组合。设ai的系数为f(i),则和式除以m的余数与ai无关当且仅当f(i)i的倍数。下面来看一个例子:

a1             a2            a3            a4           a5

a1+a2           a2+a3         a3+a4         a4+a5          

a1+2a2+a3       a2+2a3+a4      a3+2a4+a5

a1+3a2+3a3+a4    a2+3a3+3a4+a5 

a1+4a2+6a3+4a4+a5        

  看到最后的结果,“4 6 4 1”正是杨辉三角的第行。在一般情况下,最后ai的系数是Cn-1^(i-1)。问题就变成了:二项式系数中有哪些是m的倍数。

//#include<iostream>

//using namespace std;

//const int N=1000;

//int main()

//{

//int a[N];//用来存放输入的数据

//int C[N];//用来记录二项式的值

//int num;

//cout<<"请输入a数组元素个数:";

//cin>>num;

//    /*cout<<"请输入a数组元素:";//不需要输入数组元素

//for(int i=0;i<num;i++)

//cin>>a[i];*/

//    int m;

//cout<<"请输入m";

//cin>>m;

//

//    C[0]=num-1;

//for(int i=1;i<num;i++)

//{

//C[i]=C[i-1]*(num-i)/i;//原本是(num-1-i+1)

//}

//for(int i=0;i<num;i++)

//{

//if(C[i]%m==0)cout<<"无关的数是第:"<<i+1<<"个。"<<endl;

//}

//return 0;

//}

 

-5 约数的个数。

给出正整数n的唯一分解式,求n的正约数的个数。

【分析】

下面先给出一个引理-1,描述如下:

引理-1 大于的自然数都可以分解为素数的乘积。

定理-16(唯一分解定理)任何一个大于自然数n的唯一分解式

其中p1,p2,…,pkn的素数因子且p1<p2<…<pka1,a2,…,ak是非负整数,k≥

定理-17(约数个数定理)任何大于自然数n可以分解成标准分解式,则n的正约数个数为:(a1+1)(a2+1)...(ak+1)

//

//#include <stdio.h>

//#include <math.h>

//const int N = 1000;

//int e[N]={0}; //e[N]存放素因子的指数

//

//int main() {

//  int n,i,total = 1,tn;

//  int sum=0;

//  scanf("%d", &n);   //输入一个大于的自然数n

//  printf("%d=", n);

//  tn=n;

//  for(i = 2; i*i <= n; i++) //试除~sqrt(n)

//    while(n != i){

//      if(n%i == 0)//这个地方,可谓是在n的范围内把能被i正处的前面的素数的倍数都去掉了,

//  {

//        printf("%d*",i); //边计算边输出

//    e[i]++;  //素因子i对应的指数加

//        n = n/i;  //对应的n应为n/i

//  }

//      else  break;

//}

注意这个n才是最后一个质因数,最后程序结束时的n是最大的那个质因数

//  e[n]++;

//  printf("%d\n", n); 

//  //输出每个素数因子的个数,另一种形式

//printf("%d=", tn);

//  for(i = 1; i < N; i++) //输出每个素数因子的个数

//    if(e[i])  //如果in的素因子,则e[i]必不为

//      printf("%d(%d)",i,e[i]);

//  printf("\n",n);

//  for(i = 1; i < N; i++) //输出n正约数的个数

// if(e[i]) {

//sum = sum + (1 -pow((double)i,e[i]+1))/(1-i); //用等比数列公式求所有正约数之和

//total = total*(e[i]+1);//根据公式,正约数的个数等于每一项的指数加相乘。

// }

//  printf("%d\n", sum);

//  printf("%d\n", total);

//  return 0;

//}

 

 例-6 小于n且与n互素的个数。

给出正整数n的唯一分解式,求,2,3,…,n中与n互素的数的个数。

互素的数的个数可由公式得到:num(n)=n(1-1/p1)(1-1/p2)...(1-1/pk);

#include<iostream>

using namespace std;

int main()

{

int n;

cin>>n;

int ans=n;

for(int i=2;i*i<n;i++)

{

if(n%i==0)ans=ans/i*(i-1);

while(n%i==0)n/=i;//~ni的倍数全部清除掉直到ni取余不为为止

}

if(n>1)//此时是最后一个数。如果n是大于的,说明最后一个n只能被自己整除了,所以有以下代码

ans=ans/n*(n-1);

cout<<"n互素的个数是:"<<ans<<endl;

return 0;

//

//

求出~n中所有数的欧拉函数值

//#include<iostream>

//#include<cmath>

//#include<cassert>

//#include<cstring>

//using namespace std;

//int phi[8000001];

//int main(){

//int n; 

//cin >> n;

//assert(n <= 8000000);

//memset(phi, 0, sizeof(phi));

//phi[1] = 1; //O(nloglogn)

//for(int i = 2; i <= n; ++i) 

//if(!phi[i])//只算质数i,因为合数i对应的phi[i]已经被前面的质数i在内层j循环中筛完了

//

//for(int j = i; j <= n; j+=i){

//if(!phi[j]) phi[j] = j;//为每个phi[j]先初始化为j

//phi[j] = phi[j] / i * (i-1);//i(i需要是质数的i)不断的循环对phi[j]进行运算

//}

//}

//for(int i = 1; i <= n; ++i) printf("phi(%d) = %d\n", i, phi[i]);

//while(1);

//return 0;

//}  

//

 

 例-6 小于n且与n互素的个数。

给出正整数n的唯一分解式,求,2,3,…,n中与n互素的数的个数。

互素的数的个数可由公式得到:num(n)=n(1-1/p1)(1-1/p2)...(1-1/pk);

#include<iostream>

using namespace std;

int main()

{

int n;

cin>>n;

int ans=n;

for(int i=2;i*i<n;i++)

{

if(n%i==0)ans=ans/i*(i-1);

while(n%i==0)n/=i;//~ni的倍数全部清除掉直到ni取余不为为止

}

if(n>1)//此时是最后一个数。如果n是大于的,说明最后一个n只能被自己整除了,所以有以下代码

ans=ans/n*(n-1);

cout<<"n互素的个数是:"<<ans<<endl;

return 0;

//

//

求出~n中所有数的欧拉函数值

//#include<iostream>

//#include<cmath>

//#include<cassert>

//#include<cstring>

//using namespace std;

//int phi[8000001];

//int main(){

//int n; 

//cin >> n;

//assert(n <= 8000000);

//memset(phi, 0, sizeof(phi));

//phi[1] = 1; //O(nloglogn)

//for(int i = 2; i <= n; ++i) 

//if(!phi[i])//只算质数i,因为合数i对应的phi[i]已经被前面的质数i在内层j循环中筛完了

//

//for(int j = i; j <= n; j+=i){

//if(!phi[j]) phi[j] = j;//为每个phi[j]先初始化为j

//phi[j] = phi[j] / i * (i-1);//i(i需要是质数的i)不断的循环对phi[j]进行运算

//}

//}

//for(int i = 1; i <= n; ++i) printf("phi(%d) = %d\n", i, phi[i]);

//while(1);

//return 0;

//}  

//

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值