传送门UVa 10129 & HDU 1116 & POJ 1386 - Play on Words
欧拉道路的题目。
题意是给你一串英文,让你判断能不能把它们连起来。
因为只需要判断首尾字母,所以只取它们就行。
思路是先判断起点和终点是不是符合要求,即至多有两个点的入度不等于出度,而且其中一个必须入度比出度大1,另一个入度比出度小1,即起点和终点。
如果符合上面那个要求,判断图是否连通就行。
本来昨天是要做这题的,当然是不会做,因为看到了欧拉道路这个陌生的名字。
于是我又回去翻了一下小白书,发现还有两道例题我没看完。。怪不得。
搜索了一下解题报告,可以用DFS,也可以用并查集来做。。后者我还没接触到,不明觉厉。
一开始参考了@shuangde800的解题报告。第一次放UVa上,AC了。然后我看了一下程序,他的数组开到了G[100][100],实际上只要够26个英文字母就行了。然后我就改成了30,WA了。我就知道这代码有问题,但还是不能确定。在到HDU上提交了一下,WA,在POJ上,WA。所以我就开始找错了。。。
找了大概半小时,又看了几份解题报告,直到看到了@Yoangh的一句话:
//因为是有向图,如果不是欧拉回路的情况,随便找个点为起点dfs不一定能遍历所有点,如3 a b b c c d只能以a为起点否则就会判断连通性错误
然后思路就连起来了。原来是找起点这里有问题。
修过了一下,AC。
详情见代码
#include <cstdio>
#include <cstring>
using namespace std;
const int MAXN = 30;
int graph[MAXN][MAXN];
int vis[MAXN];
int in[MAXN], out[MAXN];
void DFS(int n);
int main()
{
//freopen("input.txt", "r", stdin);
int T, n, i, j, target, start, num;
char str[1005];
scanf("%d", &T);
while (T--)
{
target = start = 0;
memset(graph, 0, sizeof(graph));
memset(vis, 0, sizeof(vis));
memset(in, 0, sizeof(in));
memset(out, 0, sizeof(out));
scanf("%d%*c", &n);
for (i = 0; i < n; i++)
{
scanf("%s%*c", str);
graph[str[0] - 'a'][str[strlen(str) - 1] - 'a'] = 1;
out[str[0] - 'a']++;
in[str[strlen(str) - 1] - 'a']++;
num = str[0] - 'a';
}
bool flag = true; //这个标志用来判断是否满足度数的条件。
//接下来判断至多有两个点,一个点入度比出度多一,另一个点相反,即起点和终点
for (i = 0; i < MAXN; i++)
{
if (!flag)
break;
if (in[i] != out[i])
if (in[i] == out[i] + 1)
target++;
else if (in[i] + 1 == out[i])
{
num = i; //记录起点。
start++;
}
else
flag = false;
if (target > 1 || start > 1)
flag = false;
}
if (flag) //如果满足上面那个条件,判断是否连通。
{
DFS(num);
bool flag2 = true;
for (i = 0; i < MAXN; i++)
{
if (in[i] && !vis[i] || out[i] && !vis[i]) //如果一个点有路,确没有在DFS中被访问到,说明是断的。
{
flag2 = false;
break;
}
}
if (flag2)
printf("Ordering is possible.\n");
else
printf("The door cannot be opened.\n");
}
else
printf("The door cannot be opened.\n");
}
return 0;
}
void DFS(int n)
{
vis[n] = 1;
for (int i = 0; i < MAXN; i++)
if (graph[n][i] && !vis[i])
DFS(i);
}