7-1 正整数拆分
输入一个正整数n(2<=n<=10) , 求该数的所有不同拆分组合,要求拆分组合不能重复,例如下实例的拆分中2+4和4+2视为重复
参考思路:
所谓整数划分:指把一个正整数n写成如下形式:
- n=m1+m2+…+mi;(mi为正整数,并且1<=mi<=n),则{ m1,m2,…mi}为n的一个划分。
- 如果{ m1,m2,…mi}中的最大值不超过m,即max(m1,m2,…,i)<=m,则称它属于n的一个m划分。这里我们记n的m划分的个数为f(n,m);
- 例如n=4,它有4个划分,{3,1},{2,2},{2,1,1},{1,1,1,1};
该问题是求出n的所有划分个数,即f(n,n-1)。
下面考虑f(n,m)的方法:
根据n和m的关系,考虑有一下几种情况:
1.当n=2时,不论m的值为多少(m>0),只有一种划分:{1,1};
2.当m=1时,不论n的值为多少,也只有一种划分:n个1{1,1,1,1,1,1,…1};
3.当m=n-1时,可以分为两种情况:
(1)划分中包含n-1的情况:只有一种划分,即{n-1,1};
(2)划分中不包含n-1的情况,这时划分中最大的数字也比n-1小,即n的所有(n-1)划分,因此f(n,n-1)=1+f(n,n-2);
4.当m>n-1时,由于划分中不可能出现负数,因此相当于f(n,n-1);
5.当m<n-1时,根据划分是否包含最大值m,分为两种情况:
(1)划分中包含m的情况,即{m,{x1,x2,…xi}},其中{x1,x2,…xi}的和为n-m;因此为f(n-m,m);
(2)划分中不包含m的情况,划分中所有值都比m小,即n的(m-1)划分,个数为f(n,m-1)
即f(n,m)=f(n-m,m)+f(n,m-1);
综上所述:
n=2 or m=1 时, f(n,m) = 1;
m>=n-1 时, f(n,n-1)
m<n-1 时, f(n-m,m)+f(n,m-1);
参考代码1:递归求整数划分个数
//整数划分的递归算法
int integerDivision(int n, int m) {
if ((n<2) || (m<1)) return 0;
if ((n == 2) || (m == 1)) return 1;
if (m>=n-1) return integerDivision(n, n-1);
return integerDivision(n - m, m) + integerDivision(n, m - 1);
}
可编译参考代码2:输出划分的所有情况
深度优先遍历,利用数组记录每次划分的情况,当所有划分结果总和等于输入数据时,需输出划分结果。
int mark[256];//记录每一种划分情况
int n;//输入数据
/*
outputDivision主要记录所有划分情况并输出
假设存在一个递增的整数数组array[]={0,1,2,...n}
sum:记录index总和
k:记录当前已经记录划分的数量
prio:从后到前记录已经遍历过的array对应的index
*/
void outputDivision(int sum, int k, int prio) {
if(sum > n) {//退出条件
return;
} else if(sum == n) {//当sum(所有划分结果的总和)等于n时,需输出划分结果
int i;
if (mark[0]!=sum) {
printf("%d=",n);
for( i = 0; i < k-1; i++) {
printf("%d+",mark[i]);
}
printf("%d\n",mark[i]);
}
} else {//sum小于n时,继续向mark数组中存储结果。
for(int j = prio; j > 0; j--) {
mark[k] = j;
sum += j;
outputDivision(sum,k+1,j);//对于每次遍历,需将sum增加对应的数值,以保证进行下一步递归,同时标记划分数量+1,prio参数从n->1进行
sum -= j;//回复本递归信息
}
}
}
7-2 求迷宫最短通道
递归求解迷宫最短通道的总步长。输入一个迷宫,求从入口通向出口的可行路径中最短的路径长度。
参考思路:
本题利用深度优先遍历(DFS),寻找到能到达终点的所有可能路径对应的长度。然后记录最短路径并输出。
DFS分析:主要考虑当前应该怎么做,每到一个点下一步有上下左右四种情况,我们按下左上右四个方向来进行计算,到新的一个点判断是否走过以及有没有障碍物,如果答案是确定的就可以继续DFS。
可编译参考代码:
#include<stdio.h>
// 迷宫坐标位置类型
struct PosType
{
int x; // 行值
int y; // 列值
};
#define MAXLENGTH 10 // 设迷宫的最大行列为25
typedef int MazeType[MAXLENGTH][MAXLENGTH]; // [行][列]
// 全局变量
struct PosType end; // 迷宫终点位置
MazeType m; // 迷宫数组
int n; // 迷宫行数,列数
int min_value = 10000;
// {行增量,列增量}
struct PosType direc[4]={{0,1},{1,0},{0,-1},{-1,0}};
// 由当前位置cur、当前步骤curstep试探下一点
//curstep表示当前已经尝试的步数
void Try(struct PosType cur,int curstep)
{
int i;
struct PosType next; // 下一个位置
// 移动方向,依次为东南西北
for(i=0;i<=3;i++) // 依次试探下左上右四个方向
{
next.x=cur.x+direc[i].x;
next.y=cur.y+direc[i].y;
if(m[next.x][next.y] == 0) // 是通路
{
m[next.x][next.y]=++curstep;
if(next.x != end.x || next.y != end.y) // 没到终点
Try(next,curstep); // 试探下一点(递归调用)
else
min_value = min_value>curstep?curstep:min_value;
m[next.x][next.y]=0; // 恢复为通路,试探下一条路
curstep--;
}
}
}
int main()
{
struct PosType begin; //起点
int i,j;
while(scanf("%d",&n)!=EOF)
for(i=0;i<n;i++)
for(j=0;j<n;j++)
scanf("%d",*(m+i)+j);//二维数组的指针调用
begin.x = 1,begin.y=1;
end.x = n-2,end.y= n-2;
m[begin.x][begin.y]=2;
Try(begin,0); // 由第一步起点试探起
if(min_value == 10000)
printf("No solution");
else
printf("%d",min_value);
return 0;
}
广度优先遍历(BFS)其实更适用于最短路径的搜索,DFS和BFS的区别请参考DFS与BFS的区别
参考非递归形式的BFS代码(暂不适用于本题目,仅供思路参考)
#include <stdio.h>
char a[100][100];//根据题意自己设定数据量
int book[100][100];//标记某点是否到达
int sx,sy,gx,gy;//起始点坐标
struct node{
int x;
int y;
int s;//s表示步数
};
node q[10000];
int main(){
int head,tail;head=tail=1; //一开始清空队列
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%c",*(a+i)+j);
if(a[i][j]=='S'){
sx=i;sy=j;
}
if(a[i][j]=='G'){
gx=i;gy=j;
}
}
}
int next[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
q[tail].x=sx;q[tail].y=sy;q[tail].s=0;//将起点数据压入队列
tail++; book[sx][sy]=1;
int flag=0;//flag是为了找到最短路径后跳出while循环用的
while(head<tail){
for(int k=0;k<4;k++){
int tx=q[head].x+next[k][0],ty=q[head].y+next[k][1];
//判断是否越界
if(tx<1||tx>n||ty<1||ty>m)
continue;
if(book[tx][ty]==0&&a[tx][ty]!='#'){
q[tail].x=tx;q[tail].y=ty;q[tail].s=q[head].s+1;
book[tx][ty]=1;tail++;
}
if(tx==gx&&ty==gy){
flag=1;
break;
}
}
if(flag) break;
head++;
}
printf("%d\n", q[tail-1].s);
return 0;
}