类型1:输出划分的所有情况
本题目难度在于正整数拆分过程的输出。大部分同学都尝试了在拆分的过程中直接输出整数n,试想是否可以换成一种思路:用一个数组记录每次拆分的结果,在拆分过程中不需考虑输出,在单次拆分结束后统一输出。例如:
10=4+3+2+1 先用数组记录每次拆分的结果:array=[4,3,2,1]
10=1+1+1+1+1+1+1+1+1+1 每次拆分的结果:array=[1,1,1,1,1,1,1,1,1,1]
因此,问题就可以转换为如下两部分:
正向考虑正整数n的拆分有些困难,可以逆向思考:
假设不考虑n的大小,我们可以列举实现数组array=[n-1,n-1,…n-1,n-1,n-1]、[1,1,1,…,1,1,1]对应所有元素的和sum,判断sum与n的大小即可,大小关系分为以下三种情况:
针对以上分析,array和n可以设置为全局变量,确定递归函数divisionInteger()参数有以下几个:
整理得,伪代码大致可以如下:
variable array[ARRAY_LENGTH];
integer n;
void divisionInteger(array_len, sum, next_max_value)
{
if sum>n:
return
else if sum==n:
格式化输出n及数组
else:
for i in range(next_max_value, 1):
array[array_len] = i //记录第array_len个数为i
divisionInteger(array_len+1, sum+i, i) //由于array添加了一个数,因此sum也需要加上,由于该数组是单调递减的,因此下一个数值需要限定为不大于i
}
针对以上代码, 可以进一步尝试剪枝,不详细介绍,同学们可以自行尝试。
参考代码1:输出划分的所有情况
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;//回复本递归信息
}
}
}
类型2:递归求整数划分个数
所谓整数划分:指把一个正整数n写成如下形式:
该问题是求出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
(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
参考代码2:递归求整数划分个数
//整数划分的递归算法
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);
}
参考思路:
本题利用深度优先遍历(DFS),寻找到能到达终点的所有可能路径对应的长度。然后记录最短路径并输出。
DFS分析:主要考虑当前应该怎么做,每到一个点下一步有上下左右四种情况,我们按下左上右四个方向来进行计算,到新的一个点判断是否走过以及有没有障碍物,如果答案是确定的就可以继续DFS。
可编译参考代码:
#include
// 迷宫坐标位置类型
struct PosType
{
int x; // 行值
int y; // 列值
};
#define MAXLENGTH 10 // 设迷宫的最大行列为10
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
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;
}