算法竞赛进阶指南第二天

文章介绍了如何使用递归算法来实现组合型枚举和排列型枚举的问题,给出了具体的剪枝策略和代码示例。对于组合型枚举,通过递归并结合剪枝条件避免无效计算;对于排列型枚举,通过全排列的方式生成所有可能的序列。此外,还有一个基于状态转移的开关问题的递归解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

0x02

 1.递归实现组合型枚举

题目链接

描述

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

输入格式

两个整数n,m。

输出格式

按照从小到大的顺序输出所有方案每行1个。

首先,同一行内的数升序排列,相邻两个数用一个空格隔开。其次,对于两个不同的行,对应下标的数一一比较,字典序较小的排在前面(例如1 3 9 12排在1 3 10 11前面)。

样例输入

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 

 基于上一章的递归实现指数型枚举,我们只需要在原有的基础上进行剪枝即可

剪枝方法如下:

if(v.size()>m||v.size()+(n-x+1)<m){

        return;

}

 代码如下

#include<iostream>
#include<vector>
using namespace std;
int n,m;
vector<int>ans;
void dfs(int k){
    if(ans.size()>m||ans.size()+(n-k+1)<m){
        return;
    }
    if(k==n+1){
        for (auto i : ans)
            printf("%d ", i);
        printf("\n");
        return;
    }
    ans.push_back(k);
    dfs(k+1);
    ans.pop_back();
    dfs(k+1);
}
int main(){
    scanf("%d%d",&n,&m);
    dfs(1);
    return 0;
}

2.递归实现排列型枚举

题目链接

描述

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

输入格式

一个整数n。

输出格式

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

样例输入

3

样例输出

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

 solve函数中的参数k表示当前已经枚举了多少个数,solve函数里对1-n的数进行遍历,如果该数没有被枚举即(book[i]==0),则将其枚举

 代码如下:

#include<iostream>
using namespace std;
int n;
int book[10];
int ans[10];
void solve(int k){
    if(k==n+1){
        for(int i=1;i<n;i++){
            printf("%d ",ans[i]);
        }
        printf("%d\n",ans[n]);
        return;
    }
    for(int i=1;i<=n;i++){
        if(!book[i]){
            book[i]=1;
            ans[k]=i;
            solve(k+1);
            book[i]=0;
            ans[k]=0;
        }
    }
}
int main(){
    scanf("%d",&n);
    solve(1);
    return 0;
}

3.费解的开关

题目链接

本题为一道递推题目,我们可以容易知道,如果前i行不能再被操作了,那第i行只能通过第i+1行的操作才能改动第i行,根据这个公理,我们枚举第1行所能执行的所有操作,即从0枚举到1<<5-1,从而将第1行“固定”下来,然后根据第1行到第4行的灯的状态来修改其对应下一行位置的灯的状态

 代码如下:

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<vector>
using namespace std;
vector<vector<char>>arr(5, vector<char>(5));
vector<vector<char>>a(5, vector<char>(5));
int ans = -1;
int dir[4][2] = {
    {0,1},
    {0,-1},
    {1,0},
    {-1,0}
};
bool isborder(int i, int j) {
    return i >= 0 && i < 5 && j >= 0 && j < 5;
}
void change(int x, int y) {
    arr[x][y] = arr[x][y] == '0' ? '1' : '0';
    for (int i = 0; i < 4; i++) {
        int nx = x + dir[i][0]; int ny = y + dir[i][1];
        if (isborder(nx, ny)) {
            arr[nx][ny] = arr[nx][ny] == '0' ? '1' : '0';
        }
    }
}
void print() {
    for (int i = 0; i < 5; i++) {
        for (int j = 0; j < 5; j++) {
            printf("%c", arr[i][j]);
        }
        printf("\n");
    }
}
void solve() {
    for (int state = 0; state < 1 << 5; state++) {
        arr = a;
        int tmp = 0;
        for (int i = 0; i < 5; i++) {
            if (state & 1 << i) {
                change(0, i); tmp++;
            }
        }
        for (int i = 0; i < 5; i++) {
            for (int j = 0; j < 5; j++) {
                if (i != 4 && arr[i][j] == '0') {
                    tmp++; change(i + 1, j);
                }
                else if (i == 4 && arr[i][j] == '0') {
                    tmp = 1 << 10;
                }
            }
        }
        if (tmp <= 6) {
            if (ans == -1) {
                ans = tmp;
            }
            else {
                ans = min(ans, tmp);
            }
        }
    }
    return;
}
int main() {
    int t;
    scanf("%d", &t);
    while (t--) {
        ans = -1;
        for (int i = 0; i < 5; i++) {
            for (int j = 0; j < 5; j++) {
                cin >> a[i][j];
            }
        }
        solve();
        printf("%d\n", ans);

    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值