图的遍历课后习题

本文介绍了通过动态规划解决分数相加和为1的问题,涉及分数加法、动态转移方程以及多个实例演示,如将加号插入数字串以最小化结果,以及分数的组合求和。算法设计包括阶段划分、动态方程和程序实现,适用于分数组合与整数表达式最小值的求解。
摘要由CSDN通过智能技术生成
例1:分数相加和为1
题目:

有一个数字1,2,3,….,9组成的数字串(长度不超过200),问如何将M(M<=20)个加号插到这个数字串中,使得形成的算数表达式的值最小,请编写一个算法解决这个问题。

注意:加号不可加在字符串的最前面或者最后面,也不应该有两个或两个以上的加号相邻。M保证小于数字串的长度

算法设计
  1. 分析
  2. 利用动态规划的方法:

a) 分阶段

有几个加号就分几个阶段

b) 动态转移方程

min( function(m-1,i,k-1),num(k,j))

  1. 程序实现:
#include<stdio.h>
#define N 5
#define W 65535
int data[N] = { 7,9,8,4,6 };
int Num(int i, int j) {
	int num = 0;
	while (i<=j) {
		num = num * 10 + data[i];
		i++;
	}
	return num;
}
int function(int m, int i, int j) {
	int num = W;
	if (m == 0) {//加号没有了
		return Num(i,j);
	}
	if (j - i < m) {//数字的数量少于加号的数量,是错误的分配方式
		return;
	}
	for (int k = m; k <= j ; k++) {
		if (num > (function(m - 1, i, k  - 1) + Num(k , j))) {
			num = function(m - 1, i, k  - 1) + Num(k, j);
			printf("%d  ", Num(k + i, j));
		}
	}
	return num;
}
void main() {
	int m;
	printf("请输入有几个加号:");
	scanf_s("%d", &m);
	printf("%d",function(m, 0, N - 1));
	return;
}
例2:分数相加和为1
题目:

有分数1/2,1/3,1/4,1/5,1/6,1/8,1/10,1/12/1/15,求将其中若干个分数相加和恰好为1的组成方案,并打印成等式;例如:

1/2 + 1/3 + 1/6 = 1


在这之前,我们可以先看一下这一题 N个分数相加=1 (java实现)

算法设计:
  1. 图解

  2. 程序实现:

 #include<stdio.h>
#define N 9
int s[N] = {2,3,4,5,6,8,10,12,15};//s存储分母
/*
* z表示第z阶段;也表示a中所存储数字的最大下标
* zs表示给a数组中的第z个位置存储s中数组的第zs个数字
* a就是存储和为1的数字的字母
*/
void dfs(int z, int zs, int a[]) {
	if (zs > 9) {
		return;
	}
	if (z > 1) {
		int fz = 1, fma = a[0];
		for (int i = 1; i <= z - 1; i++) {
			fz = fma + fz * a[i];
			fma = fma * a[i];
		}
		if (fz == fma) {
			for (int i = 0; i <= z-1 ; i++) {
				if (i == z -1) {
					printf("1/%d = 1", a[i]);
				}
				else {
					printf("1/%d + ", a[i]);
				}
			}
			printf("\n");
			return;
		}
	}
	for (int i = zs; i < N; i++) {
		a[z] = s[i];
		dfs(z + 1,i + 1,a);//向下一阶段走;i控制z阶段的a中的首元素
	}
	return;
}
void main() {
	int a[N];//存储分母
	dfs(0,0, a);
	return;
}

/*问题1
* 第一次写的结果没有 1/2 + 1/3 + 1/6;就知道错了
* 输出结果:
* 1/2 + 1/3 + 1/10 + 1/15 = 1
* 1 / 2 + 1 / 4 + 1 / 10 + 1 / 12 + 1 / 15 = 1
* 1 / 3 + 1 / 4 + 1 / 6 + 1 / 10 + 1 / 12 + 1 / 15 = 1
* 仔细分析了一下是因为  在做分子分母加法的过程中 它加的其实是a数组中z-1以前的数字,
* 因为当z = i时;在进行了分子分母加法,且比较之后,才给a赋的s[z]的值;
* 即在这之前a中全是s数组中0~(z - 1)的数
* 解决方法将条件for (int i = 0; i <= z-1 ; i++)改为for (int i = 0; i <= z ; i++)后面的代码同样的道理
* 问题2
* 在解决完无法输出 1/2 + 1/3 + 1/6后,又发现 输出的结果中没有了之前第一次的结果了
* 输出结果:
* 1/2 + 1/3 + 1/6 = 1
* 1/2 + 1/4 + 1/6 + 1/12 = 1
* 之后发现先前的限制条件zs > 8在这里不行,既然加法加出来的是s数组中 z - 1之前的分子分母之和,
* 那当 z = 8时,它加出来的就是s数组中 1~7的和,
* 解决方法:条件改为 sz > 9
*/
  1. 截图
    在这里插入图片描述
例3:
题目:前i位被i整除

是否存在一个由1~9组成的9位数,每个数字只能出现一次,且这个9位数字由高位到低位前i位可以被i整除

算法设计:
  1. 图解:

  2. 程序实现:

 #include<stdio.h>
int a[9];
int Num(int z, int s[]) {
	if (z == -1) {
		return 0;
	}
	int num = 0;
	for (int i = 0; i <= z; i++) {
		num = num * 10+ s[i];
	}
	return num;
}
void Initiate() {
	for (int i = 0; i < 9; i++) {
		a[i] = 0;
	}
}
void dfs(int z,  int s[]) {
	int num = 0;
	if (z > 9) {
		return;
	}
	if (z > 0) {
		num = Num(z - 1, s);
		if (num % (z + 1) == 0 || (z + 1) ==10) {
			//printf("%d\n", num);
			for (int j = 0; j < 9; j++) {
				if (a[j] != 1) {
					s[z] = j + 1; a[j] = 1;
					dfs(z + 1, s);
					a[j] = 0;
				}
			}
			if (z == 9) {
				printf("%d\n", num);
			}
			return;
		}
		else {
			return;
		}
	 }
	else {
		for (int j = 0; j < 9; j++) {
			if (a[j] != 1) {
				s[z] = j + 1; a[j] = 1;
				dfs(z + 1, s);
				a[j] = 0;
			}
				//Initiate();
		}
	}
	return;
}
void main() {
	int s[9]; 
	dfs(0,s);
	return;
}
  1. 截图:

在这里插入图片描述

例4:
题目:n的划分

一个整数n (n <=100)可以有多种划分,分化整数之和为n,例如:

输入6

6

5 1

4 2

4 1 1

3 3

3 2 1

3 1 1 1

2 2 2

2 2 1 1

2 1 1 1 1

1 1 1 1 1 1 1

算法设计:
  1. 图解:
  2. 程序实现:
 #include<stdio.h>
int a[100];
int n;//将n设为全局变量,时因为下面的dfs函数中要使用
//1. z表示搜索的深度,以及s中数据的最大下标
//2. sd表示在在z深度中,每个搜索的开始数字
//	 就是比如在0深度搜索中
//   s中的起始数字为 1 
//   s中的起始数字为 2
//   ……
//   s中的起始数字为 6
//   即每次搜索过程中为了避免重复,起始数据必须不同,在这次的深搜过程中也不会出现sd数字以前的数字了
void dfs(int z,int sd, int s[]) {
	//对s中的数求和
	if (z >= n) {
		return;
	}
	if (z > 0) {//当s中有1个或一个以上的数字时,可以进行加法
		int num = 0;
		for (int i = 0; i <= z - 1; i++) {
			//注意为什么要 对0 ~ z -1数字进行求和,而不对0~n数字进行求和,
			//因为在第z阶段时s[z]中还没有赋值呢!所以加的必须时 0~ n-1之间的数字
			num = num + s[i];
		}
		if (num > n) {
			return;
		}
		if (num == n) {
			for (int i = 0; i <= z - 1; i++) {
				printf("%d ", s[i]);
			}
			printf("\n");
			return;
		}
	 }
	//因为有1 1 1 1 1 1所以不可以简单的根据以前题目的经验解决它
	//和之前不一样的是s中的前一位数和后一位数可以相同;
	// 怎么解决z深度之下每个搜索的起始数字不一样呢?
	// 设置sd来进行记忆
	//但是在进行下z深度下的一个搜索时,必须要排除(避开)以z深度下的上一搜索为起始数的这个数字
	for (int i = sd; i <= n; i++) {
		s[z] = i;
		dfs(z + 1,i , s);
	}
	return;
}
void main() {
	int s[100];
	printf("请输入n:");
	scanf_s("%d", &n);
	//Initiate(n);

	dfs(0,1, s);
	return;
}
  1. 截图
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值