本节课的主要内容:
数学知识的运用
1)并非数学竞赛
2)进制问题及其巧妙运用
3)整数与整除问题
4)欧几里得扩展定理
5)有理数表示,大数问题
题1:地产大亨Q先生临终的遗愿是:拿出100万元给X社区的居民抽奖,以稍慰藉心中愧疚。麻烦的是,他有个很奇怪的要求:
1. 100万元必须被正好分成若干份(不能剩余)。
每份必须是7的若干次方元。
比如:1元, 7元,49元,343元,...
2. 相同金额的份数不能超过5份。
3. 在满足上述要求的情况下,分成的份数越多越好!
请你帮忙计算一下,最多可以分为多少份?
解法:暴力枚举、七的进制
#include
#include
using namespace std;
int count = 0;
int visit[10] = {0}; //7的10次方已经超过100万
int num;
//step代表7的几次方
void dfs(int step,int count)
{
if(visit[step] >5)
{
return;
}
if(count>1000000)
{
return;
}else if(count==1000000)
{
num = 0;
for(int i=0;i<10;i++)
{
num += visit[i];
//计算份数
}
cout<
上面用到的是深度优先搜索
伪代码:
void dfs(状态A)
{
if(A不合法)
return;
if(A为目标状态)
return;
if(A不为目标状态)
dfs(A+..); //递归调用
}
以下是利用进制的转换的解法
#include
#include
#include
#include
#include
using namespace std;
void solve()
{
char arr[1024] = {0};
//将1000*1000以7进制的形式,转换成字符串存储到arr数组中
//a[0]*7^n + a[1]*7^n-1 + .... a[n-1]*7^0
//用到itoa函数
_itoa(1000*1000,arr,7);
int sum = 0;
for(int i = 0;i
itoa函数:
它的功能是将一个10进制的数转化为n进制的值、其返回值为char型。(和上面的strtol效果相反)
例如:itoa(num, str, 2); num是一个int型的,是要转化的10进制数,str是转化结果,后面的值为目标进制。
#include
#include
int main()
{
int num = 10;
char str[100];
itoa(num, str, 2);
printf("%s\n", str);
return 0;
}
题2:用天平称重时,我们希望用尽可能少的砝码组合称出尽可能多的重量。
可以假设用户的输入的数字符合范围1~121。
暴力解法(结尾的输出部分感觉有点问题)
#include
using namespace std;
int main()
{
int can[5];
//每个砝码有三种状态,要么相加,要么相减,要么啥也不干
int a[3] = {0,1,-1};
int b[3] = {0,3,-3};
int c[3] = {0,9,-9};
int d[3] = {0,27,-27};
int e[3] = {0,81,-81};
int h,i,j,k,m,n;
cin>>n;
for (i = 0; i < 3; i++)
for (j = 0; j < 3; j++)
for (k = 0; k < 3; k++)
for (m = 0; m < 3; m++)
for(h = 0; h < 3; h++)
{
if (a[i] + b[j] + c[k] + d[m] + e[h] == n)
{
can[4] = a[i];
can[3] = b[j];
can[2] = c[k];
can[1] = d[m];
can[0] = e[h];
}
}
for (i = 0; i < 5; i++)
{
if (i == 0)
cout< 0)
cout<<"+"<
进制解法
#include
#include
using namespace std;
bool cmp(int a,int b)
{
return abs(a)>abs(b);
}
int main()
{
int z,count =0;
int n;
int a[10] = {0};
int b[10] = {1,3,9,27,81};
int c[10] = {0};
cin>>n;
z = n;
while(z>=3)
{
a[count++] = z % 3;
//余数
z = z/3;
//倍数
}
if(z !=0 ){
a[count++] = z;
}
int s=0;
for(int i= 0;i < 10;i++)
{
if(a[i]!=0){
if(a[i]==1)
{
c[s++] = b[i];
}
else{
c[s++] = 0 - b[i];
a[i+1]++;
}
}
}
sort(c,c+10,cmp);
for(int i=0;i <10;i++)
{
if(i==0)
cout<0){
cout<<"+"<
题3:尼姆堆
有3堆硬币,分别是3,4,5
二人轮流取硬币。
每人每次只能从某一堆上取任意数量。
不能弃权。
取到最后一枚硬币的为赢家。
求先取硬币一方有无必胜的招法。
//模2的加法 数论 同余理论
有已证明理论,如果和为0,无论怎么拿,都会!=0 如果非0,总有办法。。。。0
三堆数目: 3 4 5
二进制 11 100 101 模2加 = 010
#include
#include
using namespace std;
void f(int *a,int len)
{
int sum = 0;
for(int i=0;i"<
题4:
如果两个数很大,怎样求最大公约数,最小公倍数?
如果是n个数呢?比如1000个数的最小公倍数
辗转相除法
欧几里得定理 gcd(A,B) = gcd(B,A%B)
算数基本定理:质因数分解的唯一性
#include
using namespace std;
int gcd(int a, int b)
{
if (b == 0)
return a;
return gcd(b, a%b);
}
int lcm(int a, int b)
{
return a * b / gcd(a, b);
}
int main()
{
cout<
题5:
从昏迷中醒来,小明发现自己被关在X星球的废矿车里。
矿车停在平直的废弃的轨道上。
他的面前是两个按钮,分别写着“F”和“B”。
小明突然记起来,这两个按钮可以控制矿车在轨道上前进和后退。
按F,会前进97米。按B会后退127米。
透过昏暗的灯光,小明看到自己前方1米远正好有个监控探头。
他必须设法使得矿车正好停在摄像头的下方,才有机会争取同伴的援助。
或许,通过多次操作F和B可以办到。
矿车上的动力已经不太足,黄色的警示灯在默默闪烁...
每次进行 F 或 B 操作都会消耗一定的能量。
小明飞快地计算,至少要多少次操作,才能把矿车准确地停在前方1米远的地方。
请填写为了达成目标,最少需要操作的次数。
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
using namespace std;
/*
解不定方程
97x + 127y = 1
欧几里得定理 ---- 辗转相除法 gcd
扩展欧几里得定理
Ax + By = gcd(A,B)
理论基础: gcd(A,B) == gcd(B,A%B)
求出特解后,通解很好表示
Ax + By = gcd(A,B)
Ax + By = gcd(B,A%B)
B(A/B x + y) + (A%B)x = gcd(B,A%B)
对比:
A/B x + y = 新x
x = 新y
*/
// 返回最大公约数
// xy: 顺便解出的xy
int e_gcd(int A, int B, int* xy,int len)
{
if (B == 0)
{
xy[0] = 1;
xy[1] = 0;
return A;
}
int ans = e_gcd(B, A%B, xy,len);
int t = xy[0];
xy[0] = xy[1];
xy[1] = t - A / B * xy[0];
return ans;
}
int main()
{
int* xy =(int *) malloc(sizeof(int)*2);
int a = e_gcd(97, 127, xy,2);
cout<