{2018.4.28}荀(gou)彧(huo)同学的递归小结

终于做到递归了啊;

很有意思的东西;

还是要做个小结;

边讲题边总结吧;

1>整数划分;

上题目:

题目描述

将正整数n表示成一系列正整数之和:n=n1+n2+?+nk,其中8>=n1≥n2≥?≥nk≥1,k≥1。正整数n的这种表示称为正整数n的划分。

例如正整数6有如下11种不同的划分:

    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。

输入格式

一个正整数 n
保证 n<=8

输出格式

一个正整数 m,表示n可以被分成m种

input

6

output

11

看着令人非常头大的东西;

讲讲思路:

在这道题里面,yu果要直接计算p(n)的划分方案数,是一个非常非常麻烦的东西,所以啊,要手动加入一个控制变量;

将最大加数不大于m的划分方案记作p(n,m)。

两个变量中间有n<m,n>=m两种情况;

(1)yu果m>n,很明显,n的拆分不可能包含大于n的加数m,此时最大加数只能是n,因此f(n,m)==f(n,n);

(2)则n可以拆分成最大加数是m的加法式。我们把加法式分成两类,分别计数;

    (i)加式中一定包含m,形如n=m+……;

         既然加式中一定包含m,那么就可以从n中减去m,然后对n-m继续拆分,是f(n-m,m)。这种情况能得到的方案数既是f(n-m,m);

    (ii)加式中不一定包含m,则最大加数最多是m-1,这种情况的方案数是f(n,m-1);

边界条件:

    f(n,1)=1    表示当最大加数是1时,整数n只有一个划分,即n个1相加;

    f(1,m)=1    表示整数n=1只有一个划分,不管最大加数m有多大;

    f(0,m)=1    表示n=0时,整数n只有一种划分;

上代码:

#include<bits/stdc++.h>
using namespace std;
int m,n,ans;
int work(int n,int m)
{
	if(n==0||m==0) return 0;
	if(n==1||m==1) return 1;
	if(n<m) return work(n,n);
	if(n==m) return work(n,n-1)+1;
	return work(n,m-1)+work(n-m,m);
}
int main()
{
	cin>>n;
	work(n,n);
	cout<<work(n,n);
	return 0;
}

其实这个代码可以变得更好看一些:

#include<bits/stdc++.h>
using namespace std;
int m,n,ans;
int work(int m,int n)
{
	if (m>n)
	{
	    return work(n,n);	
	}
	if(m==0&&n==0) return 1;
	if(m==0) return 0;
	return work(m,n-m)+work(m-1,n);
}
int main()
{
	cin>>n;
	work(n,n);
	ans=work(n,n);
	cout<<ans;
	return 0;
}

很简单很基础的一道递归题;

重点是要理解递归的概念和学会最基本的应用;

end...

2>拍卖

上题目:

题目描述

一般情况下,拍卖行的拍卖师在拍卖商品的时候都是从低价开始起拍,由买方报价,最后谁出的价格高,商品就归谁所有。但焦作有个拍卖行,拍卖师在拍卖商品时正好相反:总是从高价开始起拍,如果没有人举成交牌就降价,而且拍卖师在降价时还有规律:假如第i次报价为w元,那么第i+1次报价为w-a或者w-b元,如果降到p元时,你认为价格合适,赶快第一个举成交牌,你就花p元买下了商品。

任务:拍卖师把商品从w元降到p元的方法总数。

输入格式

文件第一行有两个正整w 和p ,第二行有有两个正整a 和b. 1 ≤ w,p ≤ 10^6  , 2 ≤ a,b ≤ 10000, a不等于b.

输出格式

文件只有一行,即所求得的方法总数。注意:测试数据中方法总数不超过MAXlongint.

样例数据

input

10  3
2  3

output

 
 

3

又是一道很那啥的题;

讲讲思路:

根据递归构建的三个条件(1.参数 2.边界 3.范围)来分析递归过程如何写;

(1)参数:我们只需要在意操作当前数就可以了,一个参数k表示当前数的值;

(2)边界:如果可以等于y,那么方法数要加1;

                   如果当前的数已经小于等于y,那么再往下递归也没有什么意义;

(3)范围:当前的数在选择的时候,可以选择减a,也可以选择减b,范围是两个数即可;

上代码:

#include<bits/stdc++.h>
using namespace std;
int x,y,a,b,ans=0;
int dfs(int k)
{
	if(k==y) return 1;
	if(k<y) return 0;
	return(dfs(k-a)+dfs(k-b));
}
int main()
{
	cin>>x>>y>>a>>b;
	dfs(x);
	cout<<dfs(x);
	return 0;
}

上面这个代码就是照着思路敲的,但是!

放到oj上提交却超时了;

还记得讲过的时空复杂度么?

在这里就要用空间换时间了;

满分代码:

include<bits/stdc++.h>
using namespace std;
int x,y,a,b,ans;
int f[1001000];
int dfs(int k)
{
	if(f[k]>=0)   return f[k];
	if(k==y) return 1;
	if(k<y) return 0;
	f[k]=dfs(k-a)+dfs(k-b);
	return f[k];
}
int main()
{
	cin>>x>>y>>a>>b;
	dfs(x);
	ans=0;
	memset(f,-1,sizeof(f));
	f[y]=1;
	cout<<dfs(x);
	return 0;
}

这就是所谓的记忆化搜索;

听老师说空间换时间原则是动态规划的出发点;

所提要加强运用;

end...

递归在我这种小菜鸡眼中可以说是很难了;

简短的两道题却包括了递归的基本思想;

希望对自己有些帮助吧;

全文终。

猜你喜欢

转载自blog.csdn.net/tereya/article/details/80139429