题目描述:
某国的足球联赛中有 N N N 支参赛球队,编号从 1 1 1 至 N N N。联赛采用主客场双循环赛制,参赛球队两两之间在双方主场各赛一场。
联赛战罢,结果已经尘埃落定。此时,联赛主席突发奇想,希望从中找出一条包含所有球队的“食物链”,来说明联赛的精彩程度。“食物链”为一个 1 1 1 至 N N N 的排列 { T 1 , T 2 , … , T N T_1,T_2,\dots,T_N T1,T2,…,TN},满足:球队 T 1 T_1 T1 战胜过球队 T 2 T_2 T2 ,球队 T 2 T_2 T2 战胜过球队 T 3 T_3 T3 ,⋯,球队 T N − 1 T_{N−1} TN−1 战胜过球队 T N T_N TN ,球队 T N T_N TN 战胜过球队 T 1 T_1 T1 。
现在主席请你从联赛结果中找出“食物链”。若存在多条“食物链”,请找出字典序最小的。
输入格式:
输入第一行给出一个整数 N N N( 2 ≤ N ≤ 20 2 \le N \le 20 2≤N≤20),为参赛球队数。随后 N N N 行,每行 N N N 个字符,给出了 N × N N \times N N×N 的联赛结果表,其中第 i i i 行第 j j j 列的字符为球队 i i i 在主场对阵球队 j j j 的比赛结果: W W W 表示球队 i i i 战胜球队 j j j, L L L 表示球队 i i i 负于球队 j j j , D D D 表示两队打平, − - − 表示无效(当 i = j i=j i=j 时)。输入中无多余空格。
输出格式:
按题目要求找到“食物链” T 1 , T 2 , ⋯ , T N T_1,T_2, ⋯, T_N T1,T2,⋯,TN ,将这 N N N 个数依次输出在一行上,数字间以 1 1 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
思路:
直接使用 d f s dfs dfs 复杂度为 O( n ! n! n!),会有一个测试点超时。我用了记忆化的方法,将 “当前搜索位置相同、且剩余未搜索的结点相同” 的状态看做是等价的。
例如:有 10 10 10 支球队,如果已访问路线为 1 , 2 , 3 , 4 1,2,3,4 1,2,3,4 并且这条路线往下搜没有可行解。那么就认为当已访问路线为 1 , 3 , 2 , 4 1,3,2,4 1,3,2,4 时,继续往下搜也不可能有可行解。
这样,等价的状态只搜索一次。
把已访问的路径状态压缩为 20 20 20 位二进制;当前位置最大取值 20 20 20,用 5 5 5 位二进制来表示。然后进行记忆化搜索。最坏情况就是所有的状态都搜索过一次,复杂度 O( n 2 n n2^n n2n)。
有很多题解用了剪枝来做这道题,剪枝条件为 “当剩余队伍中不存在战胜第一支队伍,那么这条线就没必要继续深入”。 显然这样的方法复杂度没有保证,有很多数据可以卡掉这样的程序:
20
-WWWWWWWWWWWWWWWWWWW
W-WWWWWWWWWWWWWWWWWD
WW-WWWWWWWWWWWWWWWWD
WWW-WWWWWWWWWWWWWWWD
WWWW-WWWWWWWWWWWWWWD
WWWWW-WWWWWWWWWWWWWD
WWWWWW-WWWWWWWWWWWWD
WWWWWWW-WWWWWWWWWWWD
WWWWWWWW-WWWWWWWWWWD
WWWWWWWWW-WWWWWWWWWD
WWWWWWWWWW-WWWWWWWWD
WWWWWWWWWWW-WWWWWWWD
WWWWWWWWWWWW-WWWWWWD
WWWWWWWWWWWWW-WWWWWD
WWWWWWWWWWWWWW-WWWWD
WWWWWWWWWWWWWWW-WWWD
WWWWWWWWWWWWWWWW-WWD
WWWWWWWWWWWWWWWWW-WD
WWWWWWWWWWWWWWWWWW-D
DWDDDDDDDDDDDDDDDDD-
这组数据的输出为:
1 20 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
上面的剪枝方法不能很好地处理这样的数据。
#include<bits/stdc++.h>
using namespace std;
const int N=30;
char mp[N][N];
int n,path[N],vis;
unordered_set<int> st;
bool dfs(int x,int step){
vis^=(1<<x);
if(st.count(vis|(x<<21))){ vis^=(1<<x); return false; }
st.insert(vis|(x<<21));
path[step]=x;
if(step==n&&(mp[x][1]=='W'||mp[1][x]=='L')) return true;
for(int i=1;i<=n;i++){
if(!(vis&(1<<i))&&(mp[x][i]=='W'||mp[i][x]=='L')){
if(dfs(i,step+1)) return true;
}
}
vis^=(1<<x);
return false;
}
int main(){
(cin>>n).get();
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++)
cin>>mp[i][j];
if(i!=n) cin.get();
}
bool flag=dfs(1,1);
if(flag) for(int i=1;i<=n;i++) cout<<path[i]<<" \n"[i==n];
else cout<<"No Solution\n";
}