蓝桥杯 算法提高 3000米排名预测 DFS 递归搜索 next_permutation()使用

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

const int maxn = 20 + 2;
const int maxP = 20000 + 20;
struct Predict {
    int nPre;         //n个预测
    int rSort[maxn];  //n个人编号相对排序
    int isOk;         //正确否 
} pre[maxn];          //围观人的预测
 
int n, m;               //运动员n,围观人m 
int ans;                //预测的可能数 
int predict[maxP][maxn];//正确的排名 
int pre_o[maxn];        //一次正确的排名 
bool used[maxn];        //标志数组 
void input();           //输入 
bool check(); 
void DFS(int n); 
void solve();

//输入 
void input()
{
    memset(used, 0, sizeof(used));
    scanf("%d%d", &n, &m);
    for (int i = 0; i < m; i++) {
        scanf("%d", &pre[i].nPre);
        for (int j = 0; j < pre[i].nPre; j++) {
            scanf("%d", &pre[i].rSort[j]);
        }
        scanf("%d", &pre[i].isOk);
    }
}

bool check()
{
    bool flag_1 = true,
         flag_2 = true;
    for (int i = 0; i < m; i++)
    {
        //预测正确 且 标志是正确标志 
        if (pre[i].isOk && flag_1)
        {
            int j = 0;  //代表预测数的索引 
            //k--运动员总数 
            for (int k = 0; j < pre[i].nPre && k < n; k++)  //k代表第几个选手
            {
                //预测位置 ==  运动员正确位置 
                if (pre[i].rSort[j] == pre_o[k]) {
                    j++;
                }
            } 
            if (j < pre[i].nPre) {   //有位置不等于正确排序 (pre_o和正确的预测不一致,则false) 
                flag_1 = false;
            }
        }
        else 
        {
            int j = 0;
            for (int k = 0; j < pre[i].nPre && k < n; k++) 
            {
                if (pre[i].rSort[j] == pre_o[k]) {
                    j++;
                }
            }
            if (j == pre[i].nPre) { //pre_o和不正确的预测一致,所以也应该是false 
                flag_2 = false;
            }
        }
        if (!flag_1 || !flag_2) break;   //有一个错误即错误 
    }
    if (flag_1 && flag_2) return true;
    else return false;
}

//超时??? 
void DFS(int cur)
{
    if (cur == n && check())  //结束条件
    {
        for (int i = 0; i < n; i++) {    //用dfs,亦是  所有排名按字典序依次输出。
            predict[ans][i] = pre_o[i];  //每种可能下的,排名 
        }    
        ans++;                //方案数++ 
    } 
    //当前人小于参赛总数 
    if (cur < n)
    {
        for (int i = 0; i < n; i++) 
        {
            if (!used[i])    //判断当前人是否排序过
            {
                pre_o[cur] = i;    //设置当前预测 
                used[i] = true;    //当前人标志为访问过 
                DFS(cur + 1);      //下一个人
                used[i] = false;   //重新标志为false 
            } 
        }
    }
}

void DFS_2(int n)   //用STL的话,全排列这段代码就够了
{
    for (int i = 0; i < n; i++) pre_o[i] = i;
    do {
        if (check()) {
            for (int i = 0; i < n; i++) {
                predict[ans][i] = pre_o[i];
            }
            ans++;
        }
    } while (next_permutation(pre_o, pre_o + n));
} 

//处理函数 
void solve()
{
    input();
//    DFS(0);
    DFS_2(n);
    printf("%d
", ans);
    for (int i = 0; i < ans; i++)
    {
        printf("%d", predict[i][0]);
        for (int j = 1; j < n; j++) {   //n个运动员的排名 
            printf(" %d", predict[i][j]);
        }
        printf("
");
    }
}

int main()
{
    solve();
    return 0;    
}

//一开始使用了自己写的全排列,然后对排列进行check判断,超时了.....

//然后使用了 STL里的 next_permutation(a, a + n) 然后才过

套路总结:

1. 第一步还是先写下面模板

const int maxn = 20 + 2;
const int maxP = 20000 + 20;
struct Predict {
    int nPre;         //n个预测
    int rSort[maxn];  //n个人编号相对排序
    int isOk;         //正确否 
} pre[maxn];          //围观人的预测
 
int n, m;               //运动员n,围观人m 
int ans;                //预测的可能数 
int predict[maxP][maxn];//正确的排名 
int pre_o[maxn];        //一次正确的排名 
bool used[maxn];        //标志数组 
void input();           //输入 
bool check();           //发现check()是DFS水题里面最重要的一步了!!几乎这个能想出来,整个题目就差不多了
void DFS(int n);        //自己写的全排列....超时了......
void DFS_2(int n); //next_permutation()
void solve();

//下对check进行分析

//主要就是全排列0~n-1的可能,然后用预测的结果与其比较

//预测正确, 则看是否全排列等于该预测结果,不 一致,则该排列不正确, return false

//预测错误, 则看是否全排列等于该预测结果, 一致,   则该排列不正确, return false

//check()返回true, 则应该将预测结果添加到总的predict[ans][i]中, (结果数)ans++; 

bool check()
{
    bool flag_1 = true,
         flag_2 = true;
    for (int i = 0; i < m; i++)
    {
        //预测正确 且 标志是正确标志 
        if (pre[i].isOk && flag_1)
        {
            int j = 0;  //代表预测数的索引 
            //k--运动员总数 
            for (int k = 0; j < pre[i].nPre && k < n; k++)  //k代表第几个选手
            {
                //预测位置 ==  运动员正确位置 
                if (pre[i].rSort[j] == pre_o[k]) {
                    j++;
                }
            } 
            if (j < pre[i].nPre) {   //有位置不等于正确排序 (pre_o和正确的预测不一致,则false) 
                flag_1 = false;
            }
        }
        else 
        {
            int j = 0;
            for (int k = 0; j < pre[i].nPre && k < n; k++) 
            {
                if (pre[i].rSort[j] == pre_o[k]) {
                    j++;
                }
            }
            if (j == pre[i].nPre) { //pre_o和不正确的预测一致,所以也应该是false 
                flag_2 = false;
            }
        }
        if (!flag_1 || !flag_2) break;   //有一个错误即错误 
    }
    if (flag_1 && flag_2) return true;
    else return false;
}
原文地址:https://www.cnblogs.com/douzujun/p/6654283.html