目录
题目详情:
问题描述:
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
),需确保预测序列不是排列的子序列。
- 对于正确预测(