0)
题意: 给出一些单词,能否将所有单词(每个单词都要用上,给出几次就用几次)排成一列,且第i个单词的最后一个字母总是等于第i+1个单词的第一个字母。
分析:①这道题先转化成图的模型,每个单词的首字母和尾字母是图中的点,而单词是有向边。
②对于题目所要求的,寻找是否可以走完每条边且每条边只走一次,就变成了离散数学上的对于一个图是否是欧拉图(两种,是欧拉道路和欧拉回路)的判定。
③对于无向图有两个条件:要求该图是连通的,图中全为偶数度数的点或者有最多有两个奇点 (度数为奇数的点为奇点);
对于有向图有两个条件:要求该图在忽略方向的情况下是连通的,图中全为偶数度数的点或者最多有两个奇点且其中一个点的入度比出度多1、另一个点的出度比入度多1。
④对于度数,建个数组每个点存一下出现的次数即可;对于判断图是否连通,可以用dfs或者并查集。
参考:
自己做题时: 刚开始想仅仅用dfs并不用欧拉图相关知识,即每个单词的第一个字母和最后一个字母作为点,按照有向边进行深搜,直到所有边都被纳入,但是递归最高可能10^5次会超出栈的容量。如果用不递归的写法应该很麻烦,因为题意是要把所有单词都用一遍,而并不是单纯将所有点都遍历一遍,单纯用dfs找出是否有这样的路径并不容易。况且如下面第二份Memory Limit Exceeded的代码中建立一个结构体数组,每个结构体里再建立一个字符数组,如果要满足题意则是(10^5)*(10^3)的个数,每个char两个字节(byte),就是2*(10^5)*(10^3) b,即2*(10^5)*(10^3) /1024 KB,约等于10^5KB的规模,而题目要求是小于32768KB,会超内存Memory Limit Exceeded,所以将所有边进行存储并不现实。每条边只存单词的首字母和尾字母或许可以一试.但不如用并查集更简单,只需要用两个长度大于26的数组存储26个字母即可,存储他们的入度和出度,然后10^5的边就不需要再存储了,一边输入一边再整理并查集,输入完毕以后看看所有的点的上级节点是不是同一个即可。如果单纯求图是否联通,用dfs也是可以的,与并查集都是O(n)时间复杂度,但是并查集应该稍微省一点内存(比如说,用dfs最先想到的是用二维的邻接矩阵存储(长、宽都是26)边,从任意一点出发进行深搜,每次遇到一个点就标记一下,结束之后扫描一下标记的数组,看看还有没有没标记的点。)
1)AC
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <algorithm>
#include <math.h>
#include <string>
using namespace std;
//const int maxn=100010;
const int maxn1=1010;
int flag=1;
int n;
int cnt=0;
//char zimu[26]={'a','b','c','d','e','f