https://pintia.cn/problem-sets/994805046380707840/problems/994805048175869952
L3-015 球队“食物链” (30 分)
某国的足球联赛中有NNN支参赛球队,编号从1至NNN。联赛采用主客场双循环赛制,参赛球队两两之间在双方主场各赛一场。
联赛战罢,结果已经尘埃落定。此时,联赛主席突发奇想,希望从中找出一条包含所有球队的“食物链”,来说明联赛的精彩程度。“食物链”为一个1至NNN的排列{ T1T_1T1 T2T_2T2 ⋯\cdots⋯ TNT_NTN },满足:球队T1T_1T1战胜过球队T2T_2T2,球队T2T_2T2战胜过球队T3T_3T3,⋯\cdots⋯,球队T(N−1)T_{(N-1)}T(N−1)战胜过球队TNT_NTN,球队TNT_NTN战胜过球队T1T_1T1。
现在主席请你从联赛结果中找出“食物链”。若存在多条“食物链”,请找出字典序最小的。
注:排列{ a1a_1a1 a2a_2a2 ⋯\cdots⋯ aNa_NaN}在字典序上小于排列{ b1b_1b1 b2b_2b2 ⋯\cdots⋯ bNb_NbN },当且仅当存在整数KKK(1≤K≤N1 \le K \le N1≤K≤N),满足:aK<bKa_K < b_KaK<bK且对于任意小于KKK的正整数iii,ai=bia_i=b_iai=bi。
输入格式:
输入第一行给出一个整数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_1T1 T2T_2T2 ⋯\cdots⋯ TNT_NTN,将这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;
}
}