组合型枚举题目
DFS
#include<bits/stdc++.h>
using namespace std;
int n,m;
vector<int>vec;
void dfs(int k)
{
if(vec.size()>m||vec.size()+(n-k+1)<m) return;
//到达枚举边界,输出结果并结束
if(k==n+1)
{
for(int i=0;i<vec.size();i++)
cout<<vec[i]<<" ";
cout<<endl;
return;
}
//选择这个数
vec.push_back(k);
dfs(k+1);
//回溯
vec.pop_back();
//不选择这个数
dfs(k+1);
}
int main()
{
cin>>n>>m;
dfs(1);
return 0;
}
栈(模拟机器实现)
一台典型的32位计算机采用“堆栈结构“”来实现函数调用,它在汇编语言中把函数所需的第k个、第k-1个…第1个参数依次传入栈,然后执行call指令,该指令把返回地址(当前语句的下一条语句的地址入栈,然后跳转到address位置的语句。在函数返回时,它执行ret指令。该指令把返回地址出栈,并跳转到该地址继续执行。
对于函数中定义的C++局部变量,在每次执行call与ret指令时,也会在“栈”中相应地保存与复原,而作用范围超过该函数的变量,以及通过new和molloc函数动态分配的空间则保存在另一块\称为“堆“”的结构中,栈指针、返回值、局部的运算会借助CPU的寄存器完成。
由此可知:
1.局部变量在每层递归中都占有一份空间,声明过多或递归过深就会超过栈所能储存的范围,造成栈溢出;
2.非局部变量对于各层递归都共享同一份空间,需要及时维护、还原现场,以防止在各层递归之间储存和读取的数据相互影响。
3.可以用一个数组模拟栈,使用变量模拟指针和返回值,使用switch/case或者goto/label模拟语句跳转。
#include<bits/stdc++.h>
using namespace std;
int st[100010],top,address=0,n,m;
vector<int>chosen;
void call(int x,int ret_address)
{
int old_top=top;
st[++top]=x;//储存参数x
st[++top]=ret_address;//返回地址标号
st[++top]=old_top;//在栈顶记录以前的top值
}
int ret()
{
int ret_address=st[top-1];
top=st[top];//恢复以前的top值
return ret_address;
}
int main()
{
cin>>n>>m;
call(1,0);
while(top)
{
int x=st[top-2];//获取参数
switch(address)
{
case 0:
if(chosen.size()>m||chosen.size()+(n-x+1)<m)
{
address=ret();//return
continue;
}
if(x==n+1)
{
for(int i=0;i<chosen.size();i++)
cout<<chosen[i]<<' ';
cout<<endl;
address=ret();//return
continue;
}
call(x+1,1);
address=0;
continue;//回到while循环开头,相当于开始新的递归
case 1:
chosen.push_back(x);
call(x+1,2);//返回后会执行case 2
address=0;
continue;//回到while循环开头,相当于开始新的递归
case 2:
chosen.pop_back();
address=ret();
}
}
return 0;
}
Gosper’s Hack
Gosper’s Hack是一种生成n元集合所有k元子集的算法。时间复杂度是
O
(
C
n
k
∗
n
)
O(C_{n}^{k}*n)
O(Cnk∗n)
详见此BLOG
void Gospers_Hack(int k, int n)
{
int cur=(1<<k)-1;
while(cur<1<<n)
{
int lowbit=cur&-cur;
int high=cur+lowbit;
cur=(((high^cur)>>2)/lowbit)|high;
}
}
e.g:
Gospers_Hack(3,5)
{
cur=00111;
while(cur<100000)
{
lowbit=00001;
high=01000;
high^cur=01111;
(high^cur)>>2)=00011;
(high^cur)>>2)/lowbit)=00011;(抹掉末尾0)
cur=01011;
......
}
}