搜索-递归和排列和子集(21.7.30)

搜索学的不好,重新来

递归

关键在于找出递归定义和递归终止条件。
步骤
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表示“不在”】

空集a1a2a2a1a3a3a1a3a2a3a2a1
000001010011100101110111

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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值