DFS+剪枝

题目链接:题目详情 - 7-15 球队“食物链” (30 分) (pintia.cn)

思路:

(1)题目要求按照字典序最小的顺序输出,那么第一个数字必然是1

证明(反证法):如果第一个数字不能是1,说明1没有战胜过任何队伍
那么将1填入到食物链当中,必然有一个环节不符合条件(1没有后继)
所以没有任何一种序列符合条件

(2)我们dfs从小到大搜索答案,重点在于剪枝

剪枝主要有两种情况
1.已经搜索到答案
已经搜索到答案有两种情况
(1)当前dfs搜索到答案,直接返回
(2)因为除了当前dfs,栈中还会有其他的许多dfs,这时候就会出现一种情况
当前dfs已经搜索到答案,返回。但是之后还没有执行完的dfs又搜索出了答案
那么之后搜索的出的答案就会更新之前搜索到的答案,又因为我们是按字典序搜索的
所以之后的答案字典序肯定更大,答案就会出错
所以我们要增加一个判断:当答案已经搜索到了,可以是自己也可以是被人搜索到的
那么久返回。
2.答案不存在
如果没有队伍战胜过1号队伍,那么答案肯定不存在
因为最后一个队伍与对头1无法构成食物链

AC代码

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>

using namespace std;

const int N = 110;

int n;
bool w[N][N], st[N];
vector<int> res, path;

bool check()
{
    for(int i = 1; i <= n; i ++)
        if(!st[i] && w[i][1])
            return true;
    return false;
}

void dfs(int u, vector<int> path)
{  
  
    //剪枝1.1
    if(path.size() == n - 1)
    {
        res = path;
        return ;
    }
    //剪枝1.2
    if(res.size())  return ;
    
    //剪枝2
    //该无答案判断需要放在判断有答案的前面
    //因为当答案搜索出来的时候,check一定会返回false
    if(!check()) return ;      

    
    for(int i = 1; i <= n; i ++ )
    {
        if(!st[i] && w[u][i])
        {
            st[i] = true;
            path.push_back(i);
            dfs(i, path);
            
            path.pop_back();
            st[i] = false;
        }
    }
}

int main()
{
    cin >> n;
    for(int i = 1; i <= n; i ++ )
    {
        string s;   cin >> s;
        for(int j = 0; j < s.length(); j ++ )
        {
            if(s[j] == 'W') w[i][j + 1] = 1;
            else if(s[j] == 'L') w[j + 1][i] = 1;
        }
    }
    
    st[1] = true;
    dfs(1, path);
    if(!res.size()) puts("No Solution");
    else
    {
        cout << 1 << " ";
        for(int i = 0; i < res.size(); i ++ )
        {
            cout << res[i];
            if(i != res.size() - 1) cout << ' ';
        }
    }
    
    return 0;
}

posted @ 2022-05-05 08:41  光風霽月  阅读(20)  评论(0编辑  收藏  举报