团体程序设计天梯赛 球队食物链 dfs+剪枝

18 篇文章 0 订阅

https://pintia.cn/problem-sets/994805046380707840/problems/994805048175869952

L3-015 球队“食物链” (30 分)

某国的足球联赛中有NNN支参赛球队,编号从1至NNN。联赛采用主客场双循环赛制,参赛球队两两之间在双方主场各赛一场。

联赛战罢,结果已经尘埃落定。此时,联赛主席突发奇想,希望从中找出一条包含所有球队的“食物链”,来说明联赛的精彩程度。“食物链”为一个1至NNN的排列{ T1T_1T​1​​ T2T_2T​2​​ ⋯\cdots⋯ TNT_NT​N​​ },满足:球队T1T_1T​1​​战胜过球队T2T_2T​2​​,球队T2T_2T​2​​战胜过球队T3T_3T​3​​,⋯\cdots⋯,球队T(N−1)T_{(N-1)}T​(N−1)​​战胜过球队TNT_NT​N​​,球队TNT_NT​N​​战胜过球队T1T_1T​1​​。

现在主席请你从联赛结果中找出“食物链”。若存在多条“食物链”,请找出字典序最小的。

注:排列{ a1a_1a​1​​ a2a_2a​2​​ ⋯\cdots⋯ aNa_Na​N​​}在字典序上小于排列{ b1b_1b​1​​ b2b_2b​2​​ ⋯\cdots⋯ bNb_Nb​N​​ },当且仅当存在整数KKK(1≤K≤N1 \le K \le N1≤K≤N),满足:aK<bKa_K < b_Ka​K​​<b​K​​且对于任意小于KKK的正整数iii,ai=bia_i=b_ia​i​​=b​i​​。

输入格式:

输入第一行给出一个整数NNN(2≤N≤202 \le N \le 202≤N≤20),为参赛球队数。随后NNN行,每行NNN个字符,给出了N×NN\times NN×N的联赛结果表,其中第iii行第jjj列的字符为球队iii在主场对阵球队jjj的比赛结果:W表示球队iii战胜球队jjj,L表示球队iii负于球队jjj,D表示两队打平,-表示无效(当i=ji=ji=j时)。输入中无多余空格。

输出格式:

按题目要求找到“食物链”T1T_1T​1​​ T2T_2T​2​​ ⋯\cdots⋯ TNT_NT​N​​,将这NNN个数依次输出在一行上,数字间以1个空格分隔,行的首尾不得有多余空格。若不存在“食物链”,输出“No Solution”。

输入样例1:

5
-LWDW
W-LDW
WW-LW
DWW-W
DDLW-

输出样例1:

1 3 5 4 2

输入样例2:

5
-WDDW
D-DWL
DD-DW
DDW-D
DDDD-

输出样例2:

No Solution

思路:深搜+剪枝,剪枝1:设立一个全局变量flag,当找到第一组可行解的时候,置flag为1,dfs中若flag=1则直接退出;剪枝2:当开始选第2个球队到第n个球队的时候,先看剩下没用过的球队能否战胜第一只球队,如果不能的话就没必要继续搜索下去了。有了这两个剪枝就不会TLE了,别的没什么好说的了。另外注意一下i若果战胜了j,那么可以是s[i][j]='W',也可以是s[j][i]='L'。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<cmath>
#include<map>
#include<algorithm>
#define INF 0x3f3f3f3f
typedef long long ll;
using namespace std;
                //搜索剪枝
int s[25][25];
int n;
int vis[25];
int re[25];
int flag=0;

void dfs(int cur);

int main()
{
    scanf("%d",&n);
    int cnt=0;
    for(int i=1;i<=n;i++)
    {
        getchar();
        for(int j=1;j<=n;j++)
        {
            char ch=getchar();
            if(ch=='-'||ch=='D')
                s[i][j]=0;
            else if(ch=='W')
            {
                s[i][j]=1;
                ++cnt;
            }
            else if(ch=='L')
            {
                s[i][j]=-1;
                ++cnt;
            }
        }
    }
    if(cnt>=n)
        dfs(1);
    if(!flag)
        printf("No Solution\n");
    return 0;
}

void dfs(int cur)
{
    if(cur<=n&&cur>1)
    {
        int temp=0;
        for(int i=1;i<=n;i++)
        {
            if(vis[i])
                continue;
            if(s[i][re[1]]==1||s[re[1]][i]==-1)
            {
                temp=1;
                break;
            }
        }
        if(!temp)
            return ;
    }
    if(cur>n&&(s[re[n]][re[1]]==1||s[re[1]][re[n]]==-1))
    {
        printf("%d",re[1]);
        for(int i=2;i<=n;i++)
            printf(" %d",re[i]);
        flag=1;
        return ;
    }
    if(flag)
        return ;
    for(int i=1;i<=n;i++)
    {
        if(vis[i])  //已经选过
            continue;
        vis[i]=1;
        if(cur==1)//第一层
        {
            re[cur]=i;
            dfs(cur+1);
        }
        else if(s[i][re[cur-1]]==-1||s[re[cur-1]][i]==1)
        {
            re[cur]=i;
            dfs(cur+1);
        }
        vis[i]=0;
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值