蓝桥杯算法课《算法最美》笔记——6.数学问题

6 数学问题

6.1 巧用进制

题目:天平称重:变种三进制

用天平称重时,砝码为:1 3 9 27 81 .。。。。。。。等3的指数次幂。(砝码可以放两个盘中)

题目要求实现:对用户给定的重量,给出砝码的组合方案,重量<1000000。

例如:

输入:5

输出:9-3-1

思路:二进制1001,1代表取,0代表不取,由此可以推出三进制。5=(12)(三进制)

5=(1,2)=(2,-1)=(1,-1,-1)

//直接暴力破解
#include<stdio.h>
#include<string.h>
int main()
{
	int a[3]={0,-81,81};
	int b[3]={0,-27,27};
	int c[3]={0,-9,9};
	int d[3]={0,-3,3};
	int e[3]={0,-1,1};
	int book[5];
	int i,j,k,n,p,q,flag; 
	while(scanf("%d",&n)!=EOF)
	{
		memset(book,0,sizeof(book));
		for(i=0;i<3;i++)
			for(j=0;j<3;j++)
				for(k=0;k<3;k++)
					for(p=0;p<3;p++)
						for(q=0;q<3;q++)
						{
							if(a[i]+b[j]+c[k]+d[p]+e[q]==n)
							{
								book[0]=a[i];
								book[1]=b[j];
								book[2]=c[k];
								book[3]=d[p];
								book[4]=e[q];
								break;
							}
						}
		flag=0;
		for(i=0;i<5;i++)
		{
			if(flag==0&&book[i]>0)
			{
				flag=1;
				printf("%d",book[i]);
			}
			else if(book[i]>0)
				printf("+%d",book[i]);
			else if(book[i]<0)
				printf("%d",book[i]);
		}
		printf("\n");
	}
	return 0;
}

6.2 Nim游戏

一共有N堆石子,编号1…n,第i堆中有个a[i]个石子。

每一次操作Alice和Bob可以从任意一堆石子中取出任意数量的石子,至少取一颗,至多取出这一堆剩下的所有石子。

两个人轮流行动,取走最后一个的人胜利。Alice为先手。

思路:玩家1(先手)能在非平衡的游戏中获胜,玩家2能在平衡的游戏中获胜。

若:异或和为0,玩家一取出后,一定不为0,因此玩家二可以取出部分石子使其变成0,只要玩家一取石子,玩家二就能把它弄成0,而获胜条件也是0,从而可以必胜。

若:异或和不为0,玩家一取出后,使其为0,因此玩家二可以取出部分石子使其不变成0,玩家一就能把它弄成0,而获胜条件也是0,从而可以必胜。

public class NIM游戏 {
	public static void main(String[] args) {
		int[] A = {5,10,15};
        // 101
        //1010
        //1111
        //2222
        //0000
        //平衡必输:平衡代表1的个数为偶数,所以采用异或的方式计算
		boolean res = solve(A);
		System.out.println(res);
	}

	private static boolean solve(int[] a) {
		// TODO Auto-generated method stub
		int res = 0;
		for (int i = 0; i < a.length; i++) {
			res = res^a[i];
		}
		return res !=0;
	}
}

6.3 阶梯NIM博弈

stairca Nim

奇数堆的Nim博弈:

把石子从奇数堆移动到偶数堆可以理解为拿走石子…就相当于几个奇数堆的石子在做Nim…( 如所给样例…234=5 不为零所以先手必败)

假设我们是先手,所给的阶梯石子状态的奇数堆做Nim先手能必胜,我就按照能赢的步骤将奇数堆的石子移动到偶数堆,如果对手也是移动奇数堆,我们继续移动奇数堆…如果对手将偶数堆的石子移动到了奇数堆…那么我们紧接着将对手所移动的这么多石子从那个奇数堆移动到下面的偶数堆,两次操作后,相当于偶数堆的石子向下移动了几个,而奇数堆依然是原来的样子,即为必胜的状态,就算后手一直在移动偶数堆的石子到奇数堆…我们就一直跟着他将石子继续往下移,保持奇数堆不变,如此做下去,我可以跟着后手把偶数堆的石子移动到0,然后你就不能移动这些石子了,所以整个过程…将偶数堆移动到奇数堆不会影响奇数堆做Nim博弈的过程,整个过程可以抽象为奇数堆的Nim博弈。

我们把奇数堆的石子做异或运算即可。代码同Nim游戏。

6.4 欧几里得算法

即辗转相除法。求最大公因数

//求最大公因数
public static int gcd(int m,int n){
	return n==0?m:gcd(n,m%n);
}
//求解最小公倍数
	public static long lcm(long a,long b) {
		return (a * b)/gcd(a, b);
	}

6.5 欧几里得算法扩展—求解贝祖等式

这里参考原文链接:https://blog.csdn.net/belle_mei/article/details/109342191

简单描述:ax + by = m 有整数解时,当且仅当m是d的倍数,d是a和b的最大公约数。

为了求解x,y,可以用拓展欧几里得算求解。

在这里做一下主要的分析:
(1)ax + by = gcd;
(2)bx1 + (a%b)y1 = gcd;
因为a%b = a - a/b * b,例如5 % 3 = 5 - 5/3 * 3 = 2
所以(3)gcd = bx1 + (a-(a/b)b)y1
=bx1+ay1-(a/b)by1
=ay1+b(x1 -(a/b)y1)
由式子(1)(3),可得
x = y1; y1 = x1 - (a/b)y1

public class Main {
	static long x,y;
	public static void main(String[] args) {
		linearEquation(2,7,1);
	}
	//求解最大公因数
	public static long gcd(long m,long n) {
		return n == 0?m:gcd(n, m%n);
	}
	//求解最小公倍数
	public static long lcm(long a,long b) {
		return (a * b)/gcd(a, b);
	}
	
	public static long ext_gcd(long a,long b) {
		if(b == 0) {
			x = 1;
			y = 0;
			return a;
		}
		long res = ext_gcd(b, a%b);
		long x1 = x;
		x = y;
		y = x1 - a/b * y;
		return res;
	}
	//线性方程 ax + by = m,当m是gcd(a.b)的倍数时有解
	public static long linearEquation(long a,long b,long m) {
		long d = ext_gcd(a, b);
		if(m % d != 0) {
			System.out.println("无解");
		}else {
			long n = m / d;
			x *= n;
			y *= n;
			System.out.println(x + " "+y);
		}
		return d;
	}
}

在这里我们根据代码举个例子分析:
例:2x + 7y = 1;
过程如下:
gcd(2,7) => gcd(7,2) = > gcd(2,1) = >gcd(1,0),
注意到了b==0时,x = 1,y = 0,根据前面的公式 x = y1; y1 = x1 - (a/b)y1,我们可以得到x = 0,y = 1;此时a = 7,b = 2,根据公式可 得:x = 1,y = -3;此时a = 2,b = 7,根据公式可得:x = -3,y = 1。那么结果就出来了。

6.6 欧几里得算法求解“一步之遥”

题目描述:
从昏迷中醒来,小明发现自己被关在X星球的废矿车里。
矿车停在平直的废弃的轨道上。
他的面前是两个按钮,分别写着“F”和“B”。
小明突然记起来,这两个按钮可以控制矿车在轨道上前进和后退。
按F,会前进97米。按B会后退127米。
透过昏暗的灯光,小明看到自己前方1米远正好有个监控探头。
他必须设法使得矿车正好停在摄像头的下方,才有机会争取同伴的援助。
或许,通过多次操作F和B可以办到。矿车上的动力已经不太足,黄色的警示灯在默默闪烁...
每次进行 F 或 B 操作都会消耗一定的能量。
小明飞快地计算,至少要多少次操作,才能把矿车准确地停在前方1米远的地方。

欧几里得算法看看就行了,知道原理没错,但是暴力也能求结果,能暴力我还是喜欢暴力。

public class 欧几里得求解_一步之遥 {
	public static void main(String[] args) {
		int move = 97;
		int back = -127;
		//97 * x + (-127) * y =1
		int min = 1000;
		int num;
		int bestI = 0;
		int bestJ = 0;
		for (int i = 0; i < 10000; i++) {
			for (int j = 0; j < 10000; j++) {
				if (i*move+j*back == 1) {
					num = i+j;
					if(num < min) {
						min = num;
						bestI = i;
						bestJ = j;
					}
				}
			}
		}
		
		System.out.println(min+" "+bestI+" "+bestJ);
	}
}

结果:97

6.7 求解同余方程

简单描述: a,b对m取模的结果相同,记为 a ≡ b ( mod m ) 即 a mod m = = b mod m
如果 a ≡ b ( mod m ) ,且 c ≡ d ( mod m ) ,则

  • a+b≡c+d(mod m)
  • a×b≡c×d(mod m)

做这种题还是用暴力求解,自己灵活变化。

剩下的没看

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大果壳Clap

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值