我看的b站上的这个视频,感觉讲得还不错~https://www.bilibili.com/video/BV1S24y1p7iH/?spm_id_from=333.880.my_history.page.click
题目链接:92. 递归实现指数型枚举 - AcWing题库
从 1∼n这 n个整数中 随机选取任意多个,输出所有可能的选择方案。
输入格式
输入一个整数 n。
输出格式
每行输出一种方案。
同一行内的数必须升序排列,相邻两个数用恰好 1
个空格隔开。
对于没有选任何数的方案,输出空行。
本题有自定义校验器(SPJ),各行(不同方案)之间的顺序任意。
数据范围
1≤n≤15
输入样例:
3
输出样例:
3
2
2 3
1
1 3
1 2
1 2 3
(本来尝试用画图工具的,但我还是太拉了,没有ipad好伤
#include
#include
using namespace std;
const int N =20;
int n;
int st[N];//记录状态 0表示还没考虑,1表示选,2表示不选
void dfs(int x)
{
if(x>n)//递归退出条件
{
for(int i=0;i<=n;i++)
{
if(st[i]==1)
{
cout<>n;
dfs(1);
return 0;
}
题目链接:P1706 全排列问题 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题目描述
按照字典序输出自然数 1 到 n 所有不重复的排列,即 n 的全排列,要求所产生的任一数字序列中不允许出现重复的数字。
输入格式
一个整数 n。
输出格式
由 1∼n 组成的所有不重复的数字序列,每行一个序列。
每个数字保留
5 个场宽。
输入输出样例
输入 #1复制
3
输出 #1复制
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
首先分析一下这道题叭
1.依次枚举每个位置应该放哪个数
#include
#include
using namespace std;
const int N=10;
int n;
int arr[N];
bool st[N];
void dfs(int x)//枚举位置
{
if(x>n)
{
for(int i=1;i<=n;i++)
{
printf("%5d",arr[i]);
}
printf("\n");
return ;
}
for(int i=1;i<=n;i++)//遍历数字1,2,3
{
if(!st[i])//当前数没有被用过
{
st[i]=true;
arr[x]=i;//存答案
dfs(x+1);
arr[x]=0;
st[i]=false;
}
}
}
int main()
{
cin>>n;
dfs(1);
return 0;
}
2.依次枚举每个数应该放哪个位置
#include
#include
using namespace std;
const int N=10;
int n;
int st[N];
void dfs(int x)//枚举数
{
if(x>n)
{
for(int i=1;i<=n;i++)
{
printf("%5d",st[i]);
}
printf("\n");
return ;
}
for(int i=1;i<=n;i++)//遍历位置1,2,3
{
if(st[i]==0)//当前位置没有被填上
{
st[i]=x;//填上
dfs(x+1);
st[i]=0;
}
}
}
int main()
{
cin>>n;
dfs(1);
return 0;
}
这个代码输出不是按字典序,在洛谷上不能AC(苦恼ing,求大佬指点!!!
题目链接:P1157 组合的输出 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
这题说人话,就是从n个数中取出r个,就是数学中的组合,注意数字不要求顺序。
简单画个示意图:
#include
#include
using namespace std;
const int N =20;
int n,r;
bool st[N];
int arr[N];
void dfs(int x,int start)//遍历位置 ,start代表应该从这开始遍历数
{
if(x>r)
{
for(int i=1;i<=r;i++)
{
printf("%3d",arr[i]);
}
cout<>n>>r;
dfs(1,1);
return 0;
}
简单分析一下这里为什么要传入两个参数
看运行结果明显错误的在后面出现了比他大的元素(如5,4,3),这就导致了组合出现多个。因此要额外传入一个start的限制,使其遍历后面的数字。(纯菜鸟,初学,求指点)
补充一下剪枝:
还是这个图,这里剪枝的原理是能选的数已经不够填充位置了,
题目链接:P1036 [NOIP2002 普及组] 选数 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
这种不要求顺序的,求组合数都可以这样遍历位置,dfs传入两个参数记录位置和下一位遍历数
要求顺序的排列数问题,就需要加一个if语句判断是否使用过,同样的遍历位置进行递归
#include
#include
using namespace std;
const int N =30;
int n,k;
int arr[N];//存答案
int number[N];//存数组
int cnt=0;
bool isprime(int x)
{
for(int i=2;i<=x/i;i++)
{
if(x%i==0)
{
return false;
}
}
return true;
}
void dfs(int x,int start)//遍历位置 ,start代表应该从这开始遍历数
{
if(x>k)//要选k个数
{
int sum=0;
for(int i=1;i<=k;i++)
{
sum+=arr[i];
}
if(isprime(sum)) cnt++;
return ;
}
for(int i=start;i<=n;i++)//遍历数字
{
arr[x]=number[i];
dfs(x+1,i+1);
arr[x]=0;
}
}
int main()
{
cin>>n>>k;
for(int i=1;i<=n;i++)
{
cin>>number[i];//输入数据
}
dfs(1,1);
cout<
这里学到了一个小细节 ,判断素数的函数里,一般是写成i*i<=x,这里为了防止爆数据写成i<=x/i
for(int i=2;i<=x/i;i++)
就是图上的这种情况,比如3,19这条线没有数可选了,但位置还没有填满这种分枝剪掉就可以啦;
也就是再dfs里加上一个判断,删去 已选择数+可选择数 对于选数这道题,代码实现一下看看: 剪枝大概就是这样,比想象的要简单。同时,剪枝可以缩短很多时间哦~ 题目链接:P2089 烤鸡 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 简单分析一下题目,10 种配料,每个配料1~3克,美味程度就在10~30这个区间。 10个配料,每个调料有3种选择,选择数就有3的10次方个,配料的选择之间没有关系,也就是指数型枚举。 依次枚举每个调料放几克 题目链接:P1088 [NOIP2004 普及组] 火星人 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 这题问了佬,知道了一种next_permutation库函数的方法,查了一下是c++的一个全排列函数 C++ STL全排列 next_permutation 用法 - 知乎 (zhihu.com) 题目链接:P1149 [NOIP2008 提高组] 火柴棒等式 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 题目链接:P2036 [COCI2008-2009#2] PERKET - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 补一个求绝对值:(15条消息) C\C++ 中的绝对值函数:abs()、cabs()、fabs()、labs()_c++绝对值函数_YogLn的博客-CSDN博客 题目链接:P1135 奇怪的电梯 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 每层楼最多走一次的方案一定比每层楼走多次的方案要好 被卡掉了,暴力搜索过不了,应该是要用bfs。 if((x-1)+n-start+1
#include
烤鸡问题
#include
火星人
#include
火柴棒等式
#include
PERKET
#include
奇怪的电梯
#include