0
点赞
收藏
分享

微信扫一扫

天梯赛L3-015 球队“食物链”(状态压缩、记忆化搜索)

点亮自己的那盏灯 2022-03-11 阅读 35
c++算法dfs

链接

题目描述:

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

联赛战罢,结果已经尘埃落定。此时,联赛主席突发奇想,希望从中找出一条包含所有球队的“食物链”,来说明联赛的精彩程度。“食物链”为一个 11NN 的排列 {T1,T2,,TNT_1,T_2,\dots,T_N},满足:球队 T1T_1 战胜过球队 T2T_2 ,球队 T2T_2 战胜过球队 T3T_3 ,⋯,球队 TN1T_{N−1} 战胜过球队 TNT_N ,球队 TNT_N 战胜过球队 T1T_1

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

输入格式:

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

输出格式:

按题目要求找到“食物链” T1,T2,,TNT_1​,T_2, ⋯, T_N ,将这 NN 个数依次输出在一行上,数字间以 11 个空格分隔,行的首尾不得有多余空格。若不存在“食物链”,输出 “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

思路:

直接使用 dfsdfs 复杂度为 O(n!n!)。会有一个测试点超时。我用了记忆化的方法,将 “当前搜索位置相同、且剩余未搜索的结点相同” 的状态看做是等价的。

例如:有 1010 支球队,如果已访问路线为 1,2,3,41,2,3,4 并且这条路线往下搜没有可行解。那么就认为当已访问路线为 1,3,2,41,3,2,4 时,继续往下搜也不可能有可行解。

这样,等价的状态只搜索一次。

把已访问的路径状态压缩为 2020 位二进制;当前位置最大取值 2020,用 55 位二进制来表示。然后进行记忆化搜索。最坏情况就是所有的状态都搜索过一次,复杂度 O(n2nn2^n)。

有很多题解用了剪枝来做这道题,剪枝条件为 “当剩余队伍中不存在战胜第一支队伍,那么这条线就没必要继续深入”。 显然这样的方法复杂度没有保证,有很多数据可以卡掉这样的程序:

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";
}
举报

相关推荐

0 条评论