搜索学的不好,重新来
递归
关键在于找出递归定义和递归终止条件。
步骤
1.分析问题,寻找递归条件(找出大规模问题和小规模问题之间的关系)
2.寻找边界,找出停止条件(先找出出口)
3.设计函数,即数学公式
4.如何调用递归(斐波那契要调用两次,而求阶乘只需要调用一次)
排列是有序的,子集(组合)是无序的
全排列
①(STL函数):
sort(data,data+n);
while(next_permutation(data,data+n))//把下一个排列数放在数组中
{
for(int i=1;i<=n;i++)
{ 输出 }
}
②(递归):(想象不出来就去实行一下看看,有助于理解)
int 全局的数组
int perm(int begin,int end)
{
if(begin == end) //递归结束产生一个全排列
{
for(int j=0;j<=end;j++)
cout<<data[j]<<' ';
cout<<endl;
}
else
{
for(int i=begin ; i<=end ; i++)//每次都删除一个数(这时删除的数相当于是固定不变的),将剩下的继续进行排列
{
swap(data[begin],data[i]);//把当前第一个数与后面的所有数交换位置(事实上是倒着换的)
perm(begin+1,end);//递归
swap(data[begin],data[i]);//恢复,用于下一次交换
}
}
}
子集
①:
依次选出一个元素放到集合中,直到无法继续添加元素
1
1 2
1 2 3
1 2 3 4
1 2 3 4 5
1 2 3 5
1 2 4
1 2 4 5
1 2 5
1 3
1 3 4
1 3 4 5
1 3 5
…
#include<iostream>
using namespace std;
int a[1000],t[1000];
int len;
void subset(int cur)
{
for(int i = 0; i < cur; i++)
cout<<t[a[i]]<<" ";//打印a[i]记录下标对应的当前集合
cout<< endl;
int s = cur ? a[cur-1]+1 : 0; //确定当前元素的最小下标可能值
for(int i = s; i < len; i++) //到达最大长度自动停止,返回到上次可以添加的地方
{
a[cur] = i;
subset(cur+1);
}
cout<<endl;
}
int main( ) {
cin>> len;
for(int i = 0;i < len; i++)
cin>> t[i];
subset(0);
return 0;
}
②:标记法(位向量法?)
把所有节点都执行一遍选中和不选的操作,然后输出他们相应的序列
还挺好理解的
1 2 3 4 5
1 2 3 4
1 2 3 5
1 2 3
1 2 4 5
1 2 4
1 2 5
1 2
1 3 4 5
1 3 4
1 3 5
1 3
1 4 5
…
bool b[20] = {0}; //判断当前每一个节点选中状态
void subset(int n,bool *b,int cur)//递归输出n以内所有的子集,其中b表示该节点是否选中,cur为当前下标
{
if(cur == n)//当cur加到n的时候输出
{
for (int i=0;i<n;i++)
{
if(b[i]) cout<<s[i]<<' ';
}
cout<<endl;
return ;
}
b[cur] = true;//该节点设为选中状态
subset(n,b,cur+1);//cur+1递归该状态时的下一层节点
b[cur] = false;//该节点设为不选中状态
subset(n,b,cur+1);//cur+1递归该状态时的下一层节点
}
int main() {
int len;
cin>>len;
for(int i=0;i<len;i++)
cin>>s[i];
subset(len,b,0);
}
②二进制
可以用二进制表示子集,每个子集对应一个二进制数,其中从右往左第i位(从0开始编号)表示元素i是否在集合中
子集的数量和二进制的总个数相同
例如
当n=3(a1,a2,a3)【1表示“在”,0表示“不在”】
空集 | a1 | a2 | a2a1 | a3 | a3a1 | a3a2 | a3a2a1 |
---|---|---|---|---|---|---|---|
000 | 001 | 010 | 011 | 100 | 101 | 110 | 111 |
1
2
1 2
3
1 3
2 3
1 2 3
4
1 4
2 4
1 2 4
3 4
1 3 4
…
#include <iostream>
using namespace std;
int a[1000],t[1000];
void subset(int n)
{
for(int i = 0; i <(1<<n); i++) //枚举各子集所对应的编码0, 1, 2,..., 2n-1
{
for(int j=0;j<n;j++)//对每次传入的s的二进制进行逐个位数判断
{
if(i&(1<<j)) cout<<t[j]<<" ";//从i的最低位开始逐个检查每一位,如果是1则打印
}
cout<<endl;
}
}
int main()
{
int n;
cin>>n;
for(int i=0;i<n;i++)
cin>>t[i];
subset(n);
return 0;
}