题目概述:
有一扇密码门,上面有N个字符串
每个字符串s,如果经排序后可使所有字符串首尾相接(前一个末字符和后一个首字符同),则门会打开
输入:
第一行times,下一行N,其后N行,每行s
输入有times组
限制:
1<=N<=1e5;2<=s长度<=1000;s只含小写字母;同一组数据中可能有重复的s
输出:
一个字符串,若门有可能打开,则输出
Ordering is possible.
否则输出:
The door cannot be opened.
多组输出之间没有空行
样例输入:
6
2
acm
ibm
3
acm
malform
mouse
2
ok
ok
3
yes
yes
sey
1
yes
4
no
no
no
on
样例输出:
The door cannot be opened.
Ordering is possible.
The door cannot be opened.
Ordering is possible.
Ordering is possible.
The door cannot be opened.
讨论:
一个字符串最长1000,然而仅用到首尾那两个,构成一条有向路,因此这个题虽然有十万个字符串,但是数组都近开到26,因为小写字母只有26个,也是怕混淆,没有用MAXN常量,把字母看做节点,这个题就是判断欧拉路和欧拉回路的存在性
题解状态:
280MS,1728K,2151 B,C++
#include<algorithm>
#include<string.h>
#include<stdio.h>
#include<iostream>
#include<set>
#include<map>
#include<string>
#include<vector>
using namespace std;
#define INF 0x3f3f3f3f
#define maxx(a,b) ((a)>(b)?(a):(b))
#define minn(a,b) ((a)<(b)?(a):(b))
#define MAXN 26
#define memset0(a) memset(a,0,sizeof(a))//他现在是宏函数了
struct UF//union find,并查集,复杂是因优化太多,并查集实现很多,不再注释,做成结构体是懒的写析构函数
{
int UFcnt;
int sz[MAXN];
int id[MAXN];
UF(int N)
{
UFcnt = N;
for (int p = 0; p < N; p++) {
id[p] = p;
sz[p] = 1;
}
}
inline int UFfind(int a)
{
int p = a;
while (a != id[a])
a = id[a];
id[p] = a;
return a;
}
inline bool UFconnected(int a, int b)
{
return UFfind(a) == UFfind(b);
}
inline void UFunion(int a, int b)
{
int p = UFfind(a);
int o = UFfind(b);
if (p == o)
return;
else if (sz[p] < sz[o]) {
id[p] = o;
sz[o] += sz[p];
}
else {
id[o] = p;
sz[p] += sz[o];
}
UFcnt--;
}
};
int in[26], out[26];//in degree, out degree,每个字母的入度和出度
bool marked[26];//标识哪些字母用到过,合并并查集时用
char s[1002];//string,临时存放输入字符串
bool f;//flag,就是个标记,下面再解释
void fun(int N)
{
UF uf(MAXN);//构造一个并查集
for (int p = 0; p < N; p++) {
scanf("%s", s);//input
int a = s[0] - 'a';//a只是随手起的名,将字母转换为数字便于计算
int b = s[strlen(s) - 1] - 'a';//b也是随手起的,同理
marked[a] = true;
marked[b] = true;//这两个字母出现了,标记之
uf.UFunion(a, b);//将之合并
out[a]++;//首字母出度加一
in[b]++;//末字母入度加一<span id="transmark"></span>
}//input ends here
int o = -1;
for (int p = 0; p < MAXN; p++) {//检查所有节点是否连通,即是否都在一棵树内
if (marked[p] && !f) {//数据中出现过该字母,基准字母没有找到
o = p;//这个字母就是基准字母
f = true;//已经找到基准字母
}
else if (marked[p] && f&&!uf.UFconnected(o, p)) {//在数据中出现过该字母,基准已找到,但是和基准字母不在一棵树内(图不连通)
printf("The door cannot be opened.\n");//不连通的图不存在欧拉路/回路
return;
}
}
int bigin = 0, bigout = 0;//分别记录入度比出度大一和出度比入度大一的节点数,严格卡欧拉路/回路定义,变量名也不难理解
for (int p = 0; p < MAXN; p++) {
if (in[p] != out[p]) {//入度出度不等时再深入判断,避免多个else if重复比较
if (in[p] == out[p]+1)
bigin++;
else if(out[p]==in[p]+1)
bigout++;
else {//出现了入度和出度相差大于1的情况,这不符合欧拉路/回路的定义
printf("The door cannot be opened.\n");
return;
}
}
}
if ((bigin == 1 && bigout == 1) || (!bigin&&!bigout))//是欧拉路或欧拉回路
printf("Ordering is possible.\n");
else
printf("The door cannot be opened.\n");
}
int main(void)
{
//freopen("vs_cin.txt", "r", stdin);
//freopen("vs_cout.txt", "w", stdout);
int times;
scanf("%d", ×);
while (times--) {
int N;
scanf("%d", &N);
fun(N);
memset0(in);
memset0(out);
memset0(marked);//这三个现在是宏函数了
f = false;
}
}
EOF