0x02.枚举、模拟、递推

0x02.枚举、模拟、递推

A 递归实现指数型枚举

链接:https://ac.nowcoder.com/acm/contest/998/A

从 1∼n (n≤16) 个整数中随机选取任意多个,输出所有可能的选择方案。

输入描述:

一个整数n。

输出描述:

每行一种方案。同一行内的数必须升序排列,相邻两个数用恰好1个空格隔开。对于没有选任何数的方案,
输出空行。本题有自定义校验器(SPJ),各行(不同方案)之间的顺序任意。

示例1

输入

3

输出

3

2
2 3
1
1 3
1 2
1 2 3

思路:使用状压压缩,或者递归方式求解

#include<cstdio>
int main(){
    
    
    int n;
    scanf("%d",&n);
    int sum=1<<n;
    for(int i=0;i<sum;i++){
    
    
        for(int j=0;j<n;j++){
    
    
            if((i>>j)&1)
                printf("%d ",j+1);
        }
        printf("\n");
    }
    return 0;
}
#include<iostream>
#include<string>
#include<algorithm>
#include<vector>
using namespace std;
vector <int> chose ;
 int n;
void cal(int x){
    
    
    if(x==n+1){
    
    
        for(int i=0;i<chose.size();i++)
            printf("%d ",chose[i]);          //output
            puts(" ");
            return ;               //最后结束
    }
        cal(x+1);
        chose.push_back(x);            //选择x
        cal(x+1);                  //不选
        chose.pop_back();     //回溯
}
int main(){
    
    
    cin>>n;
    cal(1);
    return 0;
}

B 递归实现组合型枚举

链接:https://ac.nowcoder.com/acm/contest/998/B

题目描述

从 1~n 这 n 个整数中随机选出 m 个,输出所有可能的选择方案。n>0n \gt 0n>0, 0≤m≤n0 \leq m \leq n0≤m≤n, n+(n−m)≤25n+(n-m)\leq 25n+(n−m)≤25。

输入描述:

两个整数n,m。

输出描述:

按照从小到大的顺序输出所有方案,每行1个。首先,同一行内的数升序排列,
相邻两个数用一个空格隔开。其次,对于两个不同的行,对应下标的数一一比较,
字典序较小的排在前面(例如1 3 9 12排在1 3 10 11前面)。

示例1

输入

5 3

输出

1 2 3
1 2 4
1 2 5
1 3 4
1 3 5
1 4 5
2 3 4
2 3 5
2 4 5
3 4 5

思路:使用递归方式枚举

#include<bits/stdc++.h>
using namespace std;
int sum,num;
vector<int> vec;
void print(){
    
    //输出方案
    for(int i=0;i< vec.size();i++)
            printf("%d ",vec[i]);
        printf("\n");
}
void dfs(int n,int k){
    
    //枚举方案
    if(n==sum+1){
    
    //结束
        if(k==num)  print();
        return ;
    }
    if(k==num){
    
     print();
        return ;
    }
    vec.push_back(n);
    dfs(n+1,k+1);
    vec.pop_back();
    dfs(n+1,k);
}
int main(){
    
    
    scanf("%d %d",&sum,&num);
    dfs(1,0);
    return 0;
}

C 递归实现排列型枚举

链接:https://ac.nowcoder.com/acm/contest/998/C

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld

题目描述

把 1∼n(n<10)个整数排成一行后随机打乱顺序,输出所有可能的次序。

输入描述:

一个整数n。

输出描述:

按照从小到大的顺序输出所有方案,每行1个。 首先,同一行相邻两个数用一个空格隔开。
其次,对于两个不同的行,对应下标的数一一比较,字典序较小的排在前面。

示例1

输入

3

输出

1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1

思路:使用数组标记选过的数字,使用递归求解,或者使用全排列函数next_permutation(a,a+n)写,STL中有全排列函数

使用递归求解

#include<bits/stdc++.h>
using namespace std;
int vis[11];//标记数组
int sum;
vector<int> vec;
void dfs(int n){
    
    
    if(n==(sum+1)){
    
    
        for(int j=0;j<vec.size();j++)
            printf("%d ",vec[j]);
        printf("\n");
        return ;
    }
    for(int i=1;i<=sum;i++){
    
    
        if(!vis[i]){
    
    
            vis[i]=1;
            vec.push_back(i);
            dfs(n+1);
            vis[i]=0;//回溯
            vec.pop_back();
        }
    }
}
int main(){
    
    
   scanf("%d",&sum);
    dfs(1);
    return 0;
}

非递归方式求全排列
参考博客:https://blog.csdn.net/qwq1503/article/details/79761435

全排列的非递归实现思想是:每次找出当前排列的下一个排列。因此,
问题就转换为,给一个排列,如何找出它的下一个排列。

例如:“95382”的下一个排列是“95823”;
“95832”的下一个排列是“98235”;
“98532”是全排列的最后一个,没有下一个全排列。

从右向左找到第一个逆序的数字,例如“95382”的第一个逆序的数字是‘3’;
从右向左找到第一个大于该逆序数字的数字,“95382”中从右向左大于‘3’
的第一个数字是‘8’;
交换这两个数字的位置,得到“95832”;
再将第一个逆序数字所在位置之后的数字翻转,即得到该排列的下一个排列,
即“95823”.

这样,给定一个排列就可以得到其下一个排列,我们就可以安装这种方法依次打印出
所有排列,就得到了所需的全排列。

代码如下:

#include<iostream>
#include<string>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;

template<typename Iterator>
bool my_next_permutation(Iterator first, Iterator last)
{
    if (first == last) // s="", s.begin()==s.end()
        return false;
    Iterator i = last;
    if (first == --i)  // s="1", s.begin()==--s.end()
        return false;
    //迭代器string.end()指向string最后一个元素的后一个位置
    //要获取最后一个元素需要回退一步,即--s.end()指向最后一个元素
    //程序运行到此处,i指向最后一个元素,因为i在第二个if语句处
    //执行了‘--i’操作

    while (true) {
        Iterator i1 = i, i2;
        if (*--i < *i1) {
            i2 = last;
            while (!(*i < *--i2))
                ;
            std::iter_swap(i, i2);
            std::reverse(i1, last);
            return true;
        }
        if (i == first) {
            std::reverse(first, last);
            return false;
        }
    }
}
int main()
{
    int n;
    scanf("%d",&n);
    std::string s = "";
    char ch;
    for(int i=1;i<=n;i++){
        ch=(char)(i+'0');
        s=s+ch;
    }
    std::sort(s.begin(), s.end());
    do{
        for(int i=0;i<s.length();i++){
            putchar(s[i]);
            putchar(' ');
        }
        putchar('\n');
    }while(my_next_permutation(s.begin(), s.end()));

    return 0;
}

输出结果为:123,132,213,231,312,321
正是我们期望的按顺序的输出。

让我们对该代码中while循环部分进行一个刨根问底的分析。
从头捋一遍,给定一个排列,获得它的下一个排列的方法为:

  • 首先,从右向左找到第一个非递增的数字的位置i,以953882为例,
    第一个非递增的数字为3,并记该数字的前一个数字的位置为i1;
  • 从最后一个数字开始往左找到第一个大于3(位置i)的数字8,
    记其位置为i2(这里的倒数第2位);
  • 交换i与i2位置的数字,得到958832;
  • 将i1到结尾的数字逆序,得到958238,此即为953882的下一个排列958238.

使用STL全排列求解

#include <algorithm>
#include<cstdio>
using namespace std;
int num[1000];
int n;
int main()
{
    
    
    scanf("%d",&n);
    for(int i=0;i<n;i++) num[i]=i+1;
    do
    {
    
    
        for(int i=0;i<n;i++)
            printf("%d ",num[i]);
        printf("\n");
    }while(next_permutation(num,num+n));
    return 0;
}

D 费解的开关

链接:https://ac.nowcoder.com/acm/contest/998/D
来源:牛客网

题目描述

你玩过“拉灯”游戏吗?25盏灯排成一个5x5的方形。每一个灯都有一个开关,游戏者可以改变它的状态。每一步,游戏者可以改变某一个灯的状态。游戏者改变一个灯的状态会产生连锁反应:和这个灯上下左右相邻的灯也要相应地改变其状态。

我们用数字“1”表示一盏开着的灯,用数字“0”表示关着的灯。下面这种状态

10111
01101
10111
10000
11011

在改变了最左上角的灯的状态后将变成:

01111
11101
10111
10000
11011

再改变它正中间的灯后状态将变成:

01111
11001
11001
10100
11011

给定一些游戏的初始状态,编写程序判断游戏者是否可能在6步以内使所有的灯都变亮。

输入描述:

第一行有一个正整数n,代表数据中共有n个待解决的游戏初始状态。
以下若干行数据分为n组,每组数据有5行,每行5个字符。每组数据描述了一个游戏的初始状态。各组数据间用一个空行分隔。
对于30%的数据,n≤5n \leq 5n≤5;
对于100%的数据,n≤500n \leq 500n≤500。

输出描述:

输出数据一共有n行,每行有一个小于等于6的整数,它表示对于输入数据中对应的游戏状态最少需要几步才能使所有灯变亮。
对于某一个游戏初始状态,若6步以内无法使所有灯变亮,请输出“-1”。

示例1

输入

3
00111
01011
10001
11010
11100

11101
11101
11110
11111
11111

01111
11111
11111
11111
11111

输出

3
2
-1

思路:枚举第一层选择开关,之后每层使上层开关打开,最后一层查看是否打开,若最后一层无法打开则无解,否则有解。为什么第二层和第三层有关呢因为第二层的点只能由第三层点亮,因为此时第一层已经是全亮了,如果从第二层入手,那么就会破坏灯全亮的状态;由此也可以递推到,n层与n+1层和n+2层的关系。这也是为什么如果前四层全亮,而第五层不是全亮就无解的原因。

#include<bits/stdc++.h>
using namespace std;
int main()
{
    
    
    int T;
    scanf("%d",&T);
    int vec[100][100];//初始图
    int tempVec[100][100];//迭代图
    int m=5;
    int n=5;
    int operatorSum;//最小步骤数
    int operatorSumTemporary;//迭代图步数
    while(T--){
    
    
            operatorSum=10000;//初始化
            operatorSumTemporary=0;
            //输入图
        getchar();//吃掉回车
        for(int i=1;i<=m;i++){
    
    //输入初始图
            for(int j=1;j<=n;j++){
    
    
                vec[i][j]=getchar();
                vec[i][j]=vec[i][j]-'0';
            }
            getchar();//吃掉回车
        }
        //枚举迭代图
        for(int k=0;k<(1<<5);k++){
    
    //枚举第一层状态
            operatorSumTemporary=0;//初始化迭代步数
            for(int i=1;i<=m;i++){
    
    //创建临时图
                for(int j=1;j<=n;j++){
    
    
                     tempVec[i][j]=vec[i][j];
                }
            }

            for(int j=1;j<=n;j++){
    
    //枚举改变的五开关的选取,单独处理第一层
                if(k>>(j-1)&1){
    
    //如果该点存在
                    operatorSumTemporary++;
                    tempVec[1][j]=!tempVec[1][j];
                    tempVec[1][j-1]=!tempVec[1][j-1];
                    tempVec[1][j+1]=!tempVec[1][j+1];
                    tempVec[2][j]=!tempVec[2][j];
                }
            }

            for(int i=1;i<=(m-1);i++){
    
    //单独处理中间几层
            for(int j=1;j<=n;j++){
    
    
                if(tempVec[i][j]==0){
    
    //灯是变亮,而不是打开
                    tempVec[i+1][j]=!tempVec[i+1][j];
                    tempVec[i+2][j]=!tempVec[i+2][j];
                    tempVec[i+1][j-1]=!tempVec[i+1][j-1];
                    tempVec[i+1][j+1]=!tempVec[i+1][j+1];
                    operatorSumTemporary++;//步骤数
                }
            }
        }

        for(int i=1;i<=n;i++){
    
    //单独处理最后一层
            if(tempVec[m][i]==0)
                operatorSumTemporary=operatorSumTemporary+m*n;//如果最后一层出现未点亮的灯,就证明图无法点亮
        }
        operatorSum =min(operatorSum,operatorSumTemporary);
    }

        if(operatorSum>6){
    
    
            printf("-1\n");
        }
        else{
    
    
            printf("%d\n",operatorSum);
        }

    }
    return 0;
}

E Strange Towers of Hanoi

链接:https://ac.nowcoder.com/acm/contest/998/E
来源:牛客网

题目描述

Background
Charlie Darkbrown sits in another one of those boring Computer Science lessons: At the moment the teacher just explains the standard Tower of Hanoi problem, which bores Charlie to death!

The teacher points to the blackboard (Fig. 4) and says: "So here is the problem:

  • There are three towers: A, B and C.

  • There are n disks. The number n is constant while working the puzzle.

  • All disks are different in size.

  • The disks are initially stacked on tower A increasing in size from the top to the bottom.

  • The goal of the puzzle is to transfer all of the disks from tower A to tower C.

  • One disk at a time can be moved from the top of a tower either to an empty tower or to a tower with a larger disk on the top.

So your task is to write a program that calculates the smallest number of disk moves necessary to move all the disks from tower A to C."
Charlie: “This is incredibly boring—everybody knows that this can be solved using a simple recursion.I deny to code something as simple as this!”
The teacher sighs: “Well, Charlie, let’s think about something for you to do: For you there is a fourth tower D. Calculate the smallest number of disk moves to move all the disks from tower A to tower D using all four towers.”
Charlie looks irritated: "Urgh. . . Well, I don’t know an optimal algorithm for four towers. . . "
Problem
So the real problem is that problem solving does not belong to the things Charlie is good at. Actually, the only thing Charlie is really good at is “sitting next to someone who can do the job”. And now guess what — exactly! It is you who is sitting next to Charlie, and he is already glaring at you.
Luckily, you know that the following algorithm works for n <= 12: At first k >= 1 disks on tower A are fixed and the remaining n-k disks are moved from tower A to tower B using the algorithm for four towers.Then the remaining k disks from tower A are moved to tower D using the algorithm for three towers. At last the n - k disks from tower B are moved to tower D again using the algorithm for four towers (and thereby not moving any of the k disks already on tower D). Do this for all k 2 ∈{1, … , n} and find the k with the minimal number of moves.
So for n = 3 and k = 2 you would first move 1 (3-2) disk from tower A to tower B using the algorithm for four towers (one move). Then you would move the remaining two disks from tower A to tower D using the algorithm for three towers (three moves). And the last step would be to move the disk from tower B to tower D using again the algorithm for four towers (another move). Thus the solution for n = 3 and k = 2 is 5 moves. To be sure that this really is the best solution for n = 3 you need to check the other possible values 1 and 3 for k. (But, by the way, 5 is optimal. . . )

输入描述:

There is no input.

输出描述:

For each n (1 <= n <= 12) print a single line containing the minimum number of moves 
to solve the problem for four towers and n disks.

思路:汉诺塔问题,由于每一个盘都要移动前k层,k聚体两次,可以得出递推方程 f[k+1]=2*f[k]+1
由秦九韶公式可以推出f[k+1]=(((((((f[1]+1)*2)+1)*2)+1)*2)+1)=2k+2k-1+2k-2+2k-3+……+21+20=2k-1(由等比数列求和公式得到)
下面如题目算法
您知道下面的算法适用于n <= 12:首先是k个>= 1个固定在塔A上的磁盘,剩余的n-k个磁盘使用针对4个塔的算法从塔A移动到塔B。然后,根据3个塔的算法,将A塔剩下的k个磁盘移到D塔。最后,使用四座塔的算法将塔B上的n - k个圆盘再次移动到塔D上(从而不移动任何已经在塔D上的k个圆盘)。对所有的k2∈{1,…都这样做, n},求出移动次数最少的k。
所以对于n = 3和k = 2,你首先需要使用针对4个塔的算法将1(3-2)个磁盘从塔A移动到塔B(一次移动)。然后你可以使用3个塔的算法将剩余的2个磁盘从塔A移动到塔D(3步)。最后一步是使用4个塔的算法将磁盘从塔B移动到塔D(另一步)。因此n = 3 k = 2的解是5步。为了确保这确实是n = 3的最佳解决方案,你需要检查k的其他可能值1和3(但顺便说一下,5是最佳的…)

#include<bits/stdc++.h>
using namespace std;
int main()
{
    
    
    int a[100];
    for(int i=1;i<=12;i++){
    
    
        a[i]=(1<<i)-1;
    }
    int b[100];
    for(int i=1;i<=12;i++)
        b[i]=10000;
    b[1]=1;
    for(int i=2;i<=12;i++){
    
    
        for(int j=1;j<i;j++){
    
    
            b[i]=min(b[i],a[i-j]+2*b[j]);
        }
    }
    for(int i=1;i<=12;i++){
    
    
        printf("%d\n",b[i]);
    }
    return 0;
}

F Sumdiv

链接:https://ac.nowcoder.com/acm/contest/998/F

题目描述

Consider two natural numbers A and B. Let S be the sum of all natural divisors of ABA^BAB. Determine S modulo 9901 (the rest of the division of S by 9901).

输入描述:

The only line contains the two natural numbers A and B, (0 <= A,B <= 50000000)separated by blanks.

输出描述:

The only line of the output will contain S modulo 9901.

示例1

输入

2 3

输出

15

说明

23=8.2^3 = 8.23=8.
The natural divisors of 8 are: 1,2,4,8. Their sum is 15. 
15 modulo 9901 is 15 (that should be output).

思路:求AB的所有约数和,先求质数,然后枚举A的素因子及其重复个数,然后计算单个素因子的所有次方的和,用快速幂累加,也可以用等比数列求和公式,但是要取模,要求除法的逆元
参考博客 https://blog.csdn.net/linyuxilu/article/details/70208516

/*
POJ 1845 Sumdiv
求A^B的所有约数之和%9901

*/
#include<stdio.h>
#include<math.h>
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;

#define MOD 9901

//******************************************
//素数筛选和合数分解
const int MAXN=10000;
int prime[MAXN+1];
void getPrime()
{
    
    
    memset(prime,0,sizeof(prime));
    for(int i=2;i<=MAXN;i++)
    {
    
    
        if(!prime[i])prime[++prime[0]]=i;
        for(int j=1;j<=prime[0]&&prime[j]<=MAXN/i;j++)
        {
    
    
            prime[prime[j]*i]=1;
            if(i%prime[j]==0) break;
        }
    }
}
long long factor[100][2];
int fatCnt;//计算素因子个数,重复素因子仅计算一次
int getFactors(long long x)
{
    
    
    fatCnt=0;
    long long tmp=x;
    for(int i=1;prime[i]<=tmp/prime[i];i++)
    {
    
    
        factor[fatCnt][1]=0;//该因子重复次数
        if(tmp%prime[i]==0)
        {
    
    
            factor[fatCnt][0]=prime[i];
            while(tmp%prime[i]==0)
            {
    
    
                factor[fatCnt][1]++;
                tmp/=prime[i];
            }
            fatCnt++;//计算素因子个数,重复素因子仅计算一次
        }
    }
    if(tmp!=1)//当素因子数过大时,记录该过大素因子
    {
    
    
        factor[fatCnt][0]=tmp;
        factor[fatCnt++][1]=1;
    }
    return fatCnt;
}

//******************************************
long long pow_m(long long a,long long n)//快速模幂运算
{
    
    
    long long res=1;
    long long tmp=a%MOD;
    while(n)
    {
    
    
        if(n&1){
    
    res*=tmp;res%=MOD;}
        n>>=1;
        tmp*=tmp;
        tmp%=MOD;
    }
    return res;
}
long long sum(long long p,long long n)//计算1+p+p^2+````+p^n
{
    
    
    if(p==0)return 0;
    if(n==0)return 1;
    if(n&1)//奇数
    {
    
    
        return ((1+pow_m(p,n/2+1))%MOD*sum(p,n/2)%MOD)%MOD;
    }
    else return ((1+pow_m(p,n/2+1))%MOD*sum(p,n/2-1)+pow_m(p,n/2)%MOD)%MOD;

}
int main()
{
    
    
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int A,B;
    getPrime();
    while(scanf("%d%d",&A,&B)!=EOF)
    {
    
    
        getFactors(A);
        long long ans=1;
        for(int i=0;i<fatCnt;i++)
        {
    
    
            ans*=(sum(factor[i][0],B*factor[i][1])%MOD);
            ans%=MOD;
        }
        printf("%lld\n",ans);
    }
    return 0;
}

G Fractal Streets

链接:https://ac.nowcoder.com/acm/contest/998/G

参考博客 : Fractal Streets (POJ3889)(分形图、递归)

https://blog.csdn.net/xuechen_gemgirl/article/details/87975396

  1. 分析:
    这是著名的通过一定规律无限包含自身的分形图。为了计算方便,我们将题目中房屋编号从0开始编号,那么S与D也都减掉1.
    大体思路:设calc(n,m)求编号为m的房屋编号在n级城市中的坐标位置,那么距离是:calc(n,s-1) 与 calc(n,d-1)的距离。
    从n(n > 1)级城市由四座n-1级城市组成,其中:
      1.左上的n-1级城市由城市结构顺时针旋转90度,从编号的顺序看,该结构还做水平翻转,坐标转换至n级时如下图。
      2与3.右上和右下和原始城市结构一样,坐标转换至n级时如下图。
      在这里插入图片描述  4.左下的n-1级城市由城市结构逆时针旋转90度,从编号的顺序看,该结构也做了水平翻转。
     
      旋转坐标的变化可通过公式:
     在这里插入图片描述在这里插入图片描述
      (设len = 2(n-1))当旋转角度是逆时针90度时,也就是顺时针270度时,(x,y)->(y, -x),然后再进行水平翻转,(y,-x)->(-y,-x)。然后再将图形平移到n级图形的左下角,在格子上的坐标变化是,水平方向增加len - 1个位置,垂直方向增加2len - 1个位置。因此坐标(x,y)按照规则转移到了(2len-1-y,len-1-x).
     注意:n-1级格子里拥有的房子数量是cnt = 22n /4,即22n-2.
        当前编号m在N级格子的哪个方位是:m / cnt.
        当前编号m在n-1级格子里的编号是: m %cnt;
    详细代码如下:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
pair<LL,LL> calc(LL n, LL m){
    
    
	if(n == 0) return make_pair(0,0);
	LL len = 1ll << (n-1) , cnt = 1ll << (2*n - 2);
	pair<LL,LL> pos = calc( n-1, m%cnt);
	LL x = pos.first , y = pos.second;
	LL z = m / cnt;//z的值代表这n-1级的(x,y)在n级城市中的方位。
	if ( z == 0 ) return make_pair(y , x); //左上 
	if ( z == 1 ) return make_pair(x, y + len);//右上 
	if ( z == 2 ) return make_pair(x+len, y + len);//右下 
	return make_pair(2*len-1-y, len - 1- x);// 左下 
}
int main(){
    
    
	int T;
	cin >> T;
	while(T--){
    
    
		LL N,A,B;
		cin >> N >> A >> B;
		pair<LL,LL> s = calc(N, A - 1);
		pair<LL,LL> d = calc(N, B - 1);
		LL ax = s.first - d.first,ay = s.second - d.second;
		cout <<fixed << setprecision(0) << sqrt(ax * ax + ay * ay)*10<< endl;
	} 
	return 0;
}

H 非递归实现组合型枚举

参考博客: https://www.cnblogs.com/TY02/p/11307274.html

链接:https://ac.nowcoder.com/acm/contest/998/H
来源:牛客网

从 1~n 这 n 个整数中随机选出 m 个,输出所有可能的选择方案。n>0n \gt 0n>0, 0≤m≤n0 \leq m \leq n0≤m≤n, n+(n−m)≤25n+(n-m)\leq 25n+(n−m)≤25。

输入描述:

两个整数n,m。

输出描述:

按照从小到大的顺序输出所有方案,每行1个。首先,同一行内的数升序排列,相邻两个数用一个空格隔开。其次,对于两个不同的行,
对应下标的数一一比较,字典序较小的排在前面(例如1 3 9 12排在1 3 10 11前面)。

示例1

输入

5 3

输出

1 2 3
1 2 4
1 2 5
1 3 4
1 3 5
1 4 5
2 3 4
2 3 5
2 4 5
3 4 5
#include <iostream>  
#include <cstring>  
#include <cstdio>  
#include <vector>  
using namespace std;  
vector<int> chosen;  
int top, address, sta[100010], n, m;  
inline void call(int x, int ret_addr) {
    
      //模拟系统栈指令call(),记录每个状态的参数和返回语句位置
    int pre = top;  
    sta[++top] = x;  //当前选择的数
    sta[++top] = ret_addr;  
    sta[++top] = pre;  //前面的容量
}  
inline int ret() {
    
      //模拟指令return,退栈并返回应该执行的下一条语句
    int ret_addr = sta[top - 1];  
    top = sta[top];  
    return ret_addr;  
}  
int main() {
    
      
    cin >> n >> m;  
    call(1, 0);  
    while (top) {
    
      
        int x = sta[top - 2];  
        switch (address) {
    
      
            case 0:  
                if (chosen.size() > m || chosen.size() + (n - x + 1) < m) {
    
      
                    address = ret();  
                    continue;  
                }  
                if (x == n + 1) {
    
      
                    for (int i = 0; i < chosen.size(); ++i)  
                        printf("%d ", chosen[i]);  
                    puts("");  
                    address = ret();  
                    continue;  
                }  
                chosen.push_back(x);  
                call(x+1, 1);  //入栈下一个状态
                address = 0;  //下一个函数从头执行
                continue;  
            case 1:  
                chosen.pop_back();  
                call(x+1, 2);  //入栈下一个状态
                address = 0;  
                continue;  
            case 2:  
                address = ret();  //当前状态已执行完毕,返回
        }  
    }  
    return 0;     
}  

猜你喜欢

转载自blog.csdn.net/ZXG20000/article/details/113524608