【DFS全排列+判断子序列】蓝桥杯算法提高-3000米排名预测

目录

题目详情:

问题描述:

输入说明:

输出说明:

测试用例:

输入:

输出:

题解:

问题分析:

题解代码:

代码详解:

1. 输入处理

2. 全排列生成(DFS)

3. 条件验证(check函数


题目详情:

问题描述:

        

        3000米长跑时,围观党们兴高采烈地预测着最后的排名。因为他们来自不同的班,对所有运动员不一定都了解,于是他们分别对自己了解的一些运动员的实力作出了评估,即对部分运动员做了相对排名的预测,并且告诉了可怜留守的班长。因为无聊,于是他们就组团去打Dota去了。比赛结束后他们向班长询问最后的排名,但班长不记得了,只记得他们中哪些人的预测是正确的,哪些人的预测是错误的。他们想知道比赛的排名可能是什么。
 

输入说明:

第一行两个整数n, m,n为运动员数量,m为围观党数量。运动员编号从0到n-1。

接下来m行,每行为一个围观党的相对排名预测。每行第一个数c表示他预测的人数,后面跟着c个0~n-1的不同的数,表示他预测的运动员相对排名,最后还有一个数,0表示这个预测是错误的,1表示是正确的。

数据规模和约定

  1<=n<=10, 2<=c<=n, 1<=m<=10,保证数据合法,且答案中排名可能数不超过20000。对于一个排名序列,一个预测是正确的,当且仅当预测的排名的相对顺序是排名序列的一个子序列。一个预测是错误的,当且仅当这个预测不正确。

输出说明:

第一行一个数k为有多少种排名的可能。

下面k行,每行一个0~n-1的排列,为某一个可能的排名,每个数字后有一个空格(即:行尾也有一个空格)。所有排名按字典序依次输出。

比如,输入:

3 2

2 0 1 1

2 2 1 0

则输出:

1

0 1 2 

测试用例:

输入:

3 2
2 0 1 1
2 1 2 0

输出:

2
0 2 1 
2 0 1 
 

题解:

问题分析:

        ①:所有的排名情况 很明显就是全排列问题。比如0-5 6名运动员的所有排名可能,就是0-5 六个数字的全排列。全排列很明显可以用DFS实现。

        ②:那么对于每一种排列情况 我们需要判断其符不符合班级同学的预测情况。所以我们要有一个检查函数check  check函数的思路:

        a:对于预测结果对的情况,要求所有预测结果是对的序列,都满足是这个全排列的子序列。

        b:对于预测结果错的情况,要求所有预测结果是错的序列,都不能是这个全排列的子序列。

        c:所以一个排列,要用所有预测情况来检查排列,满足全部a和b的情况 才能return 1 说明是有可能的排名情况,如果在a b中某一项就不成立,则直接return 0 退出,不用接下来的判断了。

        ③:如何判断一个序列是不是另一个排列的子序列呢(不一定要连续) 由于这个序列的长度已知,假设为k 我们只要用两个指针i j 。用i指针遍历排列,j来记录序列下标。若pailie[i]==xulie[j] 则j++。最后遍历完排列,若j==k 则说明是子序列 否则不是。

题解代码:

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<vector>
#include<queue>
#include<cmath>
#include<algorithm>
using namespace std;
int n,m;
int tf[20];//预测正确与否数组
int yuce[20][20];//预测的顺序
int renshu[20];//预测的人数
int num=0;//答案的个数
bool vis[20];//全排列访问数组
int ans[20005][20];//答案数组
int pailie[20];//全排列
bool check(){//检查这种排列是否满足预测  //也是一种检查是否是子序列的办法
    for(int i=0;i<m;++i){
        //m个人的预测都要过一遍
        int j=0;//检测是否满足预测的人数
        if(tf[i]){//如果这个人的预测是正确的
            for(int k=0;k<n&&j<renshu[i];++k){
                if(pailie[k]==yuce[i][j])//遍历这种排列的每一位
                {
                    j++;
                }
            }
            if(j!=renshu[i])
                return 0;//如果不满足 那么肯定是错误的排列 满足也不一定是对的 要满足所有的正确的才是
        }
        else{//如果这个人的预测是错误的
            for(int k=0;k<n&&j<renshu[i];++k){
                if(pailie[k]==yuce[i][j])//遍历这种排列的每一位
                {
                    j++;
                }
            }
            if(j==renshu[i])
                return 0;//一个预测结果不是对的就是错的 所以如果是错的 j肯定会到达renshu[i]的个数
        }
    }
    return 1;//把每一个的预测都对照一编 才能得出正确结果
}
void dfs(int floor){
    if(floor==n&&check()){
        num++;//答案个数
        for(int i=0;i<n;++i){
            ans[num][i]=pailie[i];//记录下这种正确的排列
        }
        return;
    }
    for(int i=0;i<n;++i){//全排列
        if(!vis[i]){
            pailie[floor]=i;
            vis[i]=1;
            dfs(floor+1);
            vis[i]=0;//回溯
        }
    }
    return ;
}
int main()
{
    cin>>n>>m;
    for(int i=0;i<m;++i){
        cin>>renshu[i];//输入预测的人数
        for(int j=0;j<renshu[i];++j)
            cin>>yuce[i][j];
        cin>>tf[i];//正确与否
    }
    dfs(0);
    cout<<num<<endl;
    for(int i=1;i<=num;++i){
        for(int j=0;j<n;++j){
            cout<<ans[i][j]<<" ";
        }
        if(i!=num)
            cout<<endl;
    }
    return 0;
}













代码详解:

1. 输入处理
  • 功能:读取输入数据,包括运动员数量n、预测数量m,以及每个预测的运动员序列和正确性标记。
  • 关键点yuce数组存储每个预测的运动员序列,tf数组记录预测是否正确。
2. 全排列生成(DFS)

  • 功能:通过深度优先搜索生成所有可能的排列。
  • 关键点
    • vis数组标记是否已选过当前运动员。
    • pailie数组动态记录当前排列。
    • 递归终止条件:当floor == n时,调用check()验证排列是否满足所有预测条件。
3. 条件验证(check函数

  • 功能:验证当前排列是否符合所有预测的正确性要求。
  • 关键点
    • 对于正确预测(tf[i] == 1),需确保预测序列是排列的子序列。
    • 对于错误预测(tf[i] == 0),需确保预测序列不是排列的子序列。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值