例1:分数相加和为1
题目:
有一个数字1,2,3,….,9组成的数字串(长度不超过200),问如何将M(M<=20)个加号插到这个数字串中,使得形成的算数表达式的值最小,请编写一个算法解决这个问题。
注意:加号不可加在字符串的最前面或者最后面,也不应该有两个或两个以上的加号相邻。M保证小于数字串的长度
算法设计
- 分析
- 利用动态规划的方法:
a) 分阶段
有几个加号就分几个阶段
b) 动态转移方程
min( function(m-1,i,k-1),num(k,j))
- 程序实现:
#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实现)
算法设计:
-
图解
-
程序实现:
#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
*/
- 截图
例3:
题目:前i位被i整除
是否存在一个由1~9组成的9位数,每个数字只能出现一次,且这个9位数字由高位到低位前i位可以被i整除
算法设计:
-
图解:
-
程序实现:
#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;
}
- 截图:
例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
算法设计:
- 图解:
- 程序实现:
#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;
}
- 截图