寒假之(递归与分治)

递归的思想就把问题简单化,分为更小的子问题,一个一个去解决,每种情况之间还会回溯,分出所有的情况,对于一些数据量小的问题,可以用全递归,对于数据量大的,就可以用DP(记忆化搜索)(现在不提)。穷尽搜索时可以考虑递归算法。具有一定的回退性。

分治与递归是分不开的,将问题分为局部问题。递归求解局部为。将局部问题”整合“,解决原问题。

看道题目:

编写一个程序,读取n个元素和整数m的序列a,如果可以通过在a中添加元素来生成m,则输出“yes”,否则输出“no”。一个元素只能使用一次。              

在每个问题中都包含mi的顺序A和Q问题。              

输入              

在第一行给出n。在第二行中,给出n个整数。第三行中给出了q。然后,在第四行中,给出q整数(mi)。              

输出              

对于每个问题mi,打印“是”或“否” 

Constraints

  • n ≤ 20
  • q ≤ 200
  • 1 ≤ elements in A ≤ 2000
  • 1 ≤ Mi ≤ 2000

Sample Input 1

5
1 5 7 10 21
8
2 4 17 8 22 21 100 35

Sample Output 1

no
no
yes
yes
yes
yes
no
no

首先看题,不知道怎么下手,怎么用循环把所有的情况列举出来,再来判断?有点困难,题解用递归实现穷举,将情况全部都判断一遍,递归有个好处,可以回溯返回上一层的情况,接着改变状态,再判断一次,看看能否满足条件。第一个数列的每个数据都有两种状态,可以选,也可以不选。于是,递归的这个方程就是将所有情况都考虑进去,就可以实现穷举所有情况。(需要将所有情况都考虑进去)。

如果选择当前数据,就 当前的满足条件的和(每次递归都会不一样的,状态不一样,类似于状态转移方程,每次的状态都会改变)。

代码就会出来:

#include<stdio.h>
#include<iostream>
using namespace std;
#include<string>
#include<string.h>
#include<algorithm>
#include<queue>
#include<vector>
#include<list>
#include<set>
const int maxn=100005;
typedef long long ll;
int n;
int	a[maxn];
int solve(int i,int m)
	{
		if(m==0)
			return 1;
		if(i>=n)
			return 0;
			int res=solve(i+1,m)||solve(i+1,m-a[i]);
		return res;
	}
int main()
{
	int q,m,i;
	cin>>n;
	for(i=0;i<n;i++)
		cin>>a[i];
	cin>>q;
	for(i=0;i<q;i++)
		{
			cin>>m;
			if(solve(0,m))
				cout<<"yes"<<endl;
			else
				cout<<"no"<<endl;
		}
	return 0;
}

用1来表示被选中,0表示没被选中。solve(i+1,m) || solve(i+1,m-a[i])  是两种状态,可以选,也可不选,穷尽搜索将所有情况考虑进去。

放苹果 POJ 1664 

Description

把M个同样的苹果放在N个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?(用K表示)5,1,1和1,5,1 是同一种分法。

Input

第一行是测试数据的数目t(0 <= t <= 20)。以下每行均包含二个整数M和N,以空格分开。1<=M,N<=10。

Output

对输入的每组数据M和N,用一行输出相应的K。

Sample Input

17 3

Sample Output

8

同样采取 递归穷尽搜索的方法,来得到所有的可能, 这就需要将所有的分的方法都要想到,大致可以分为 1.至少有一个空盘子 2.没有空盘子。如果盘子数量大于苹果的数量,就可以把多余的盘子扔掉(因为没有用)。思路大概是这个样子。  第一种情况,可以有盘子为空的情况,那就在别的盘里面多放几个,盘子数量减减,直到盘子就为一个的时候,这就到达终点了,将所有苹果全放到这一个盘子里面。2.第二种情况就是,没有盘子为空的,所有盘子都有苹果,那就把余下的苹果再按照这几个盘子再去分,就会达到每个盘子数量不一样的情况,直到分的苹果最后为0 的时候,说明苹果一遍一遍的  以一个为单位放到每个盘子情况结束。

因为递归有回溯的功能,这两种情况会不断地重叠,交叉,来达到穷举的效果。

想到这里,代码基本上就会出来了:

代码:

 

#include<iostream>
#include<algorithm>
#include<string.h>
#include<string>
const int maxn=1000100;
using namespace std;
typedef long long ll;
const int mod=1000000000;
ll fun(ll m,ll n)
{
	if(m==0||n==1)  
		return 1;
	if(m<n)
		fun(m,m);     
	else
		return fun(m,n-1)+fun(m-n,n);    
}
int main()
{
	int t;
	cin>>t;
	ll m,n;
	while(t--)
	{
		cin>>m>>n;
		cout<<fun(m,n)<<endl;
	}
	return 0;
}

 基本上的思想就是这样子。但是分治与递归的题型会很多。就在训练题中一个一个遇见吧。

递归与分治练习题目 HDU 4643 2050 POJ 2586 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值