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','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
int pre[30];
int rudu[30];
int chudu[30];
/*
struct Code{
char letter[maxn1];
char first_letter;
char last_letter;
int len;
int bj;
}code[maxn];
*/
int Find(int a){//非递归,路径压缩
int root=a;
while(pre[root]!=root){
root=pre[root];
}
int temp;
while(pre[a]!=a){
temp=pre[a];
pre[a]=root;
a=temp;
}
return a;
}
void Join(int a,int b){
int p1=Find(a);
int p2=Find(b);
if(p1==p2){//不能替换为 if(pre[a]==pre[b]),Find()函数仍然是必要的,否则失去路径压缩的意义同时也得不到正确答案,比如Join(1,2) 1->2; Join(1,3) 1->2,2->3; Join(1,4) 1->2,2->4 [而此时,实际上应该1->2,2->3,3->4,因为不是Find()找到最高负责人,只是匆匆找到上一个负责人改变指向,从而出错]
return ;
}
pre[p1]=p2;//简单合并,非按秩合并
return ;
}
int main()
{
int kase;
cin>>kase;
//char g1='a';
//int g2=g1-97;
//cout<<g2<<endl;
while(kase--){
flag=1;
cnt=0;
scanf("%d",&n);
memset(rudu,0,sizeof(rudu));
memset(chudu,0,sizeof(chudu));
///并查集,判断是否是连通图
for(int i=0;i<26;i++)
pre[i]=i;
char s[maxn1];
int s_len;
for(int i=0;i<n;i++){
scanf("%s",s);
s_len=strlen(s);
int one=s[0]-'a';
int sec=s[s_len-1]-'a';
chudu[one]++;
rudu[sec]++;
Join(one,sec);
}
for(int i=0;i<26;i++){
if(rudu[i]>0||chudu[i]>0){
if(pre[i]==i){
cnt++;
}
}
}
if(cnt>1){
cout<<"The door cannot be opened."<<endl;
continue;
}
///判断是否是欧拉图(包括欧拉道路和欧拉回路,后者图中没有奇数点,前者图中只有一对奇数点且)
int sum=0;
for(int i=0;i<26;i++){
if(sum>2){
flag=0;
break;
}
if(rudu[i]!=chudu[i]){
sum++;
if(fabs(rudu[i]-chudu[i])!=1){
flag=0;
break;
}
}
}
if(flag==1){
cout<<"Ordering is possible."<<endl;
}
else{
cout<<"The door cannot be opened."<<endl;
}
}
return 0;
}
2)Memory Limit Exceeded:
#include <string.h>
#include <stdio.h>
#include <algorithm>
using namespace std;
const int maxn=100010;
const int maxn1=1010;
int flag=0;
int n;
int sum=0;
//char zimu[26]={'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
struct Code{
char letter[maxn1];
char first_letter;
char last_letter;
int len;
int bj;
}code[maxn];
struct Pos{
int star;
int endd;
}pos[27];//'a'==97,'a'-97==0
bool cmp(Code a,Code b){
if(a.letter[0]<b.letter[0]){
return 1;
}
return 0;
}
void Dfs(int id){
if(flag==1){
return ;
}
if(sum==n){
flag=1;
return ;
}
int cur_star=pos[code[id].last_letter-97].star;
int cur_end=pos[code[id].last_letter-97].endd;
if(cur_star==0){
return ;
}
for(int i=cur_star ;i<=cur_end;i++){
if(code[i].bj==0){
code[i].bj=1;
sum++;
Dfs(i);
}
}
return ;
}
int main()
{
int kase;
cin>>kase;
//char g1='a';
//int g2=g1-97;
//cout<<g2<<endl;
while(kase--){
flag=0;
scanf("%d",&n);
memset(pos,0,sizeof(pos));
for(int i=0;i<n;i++){
scanf("%s",code[i].letter);
code[i].len=strlen(code[i].letter);
code[i].first_letter=code[i].letter[0];
code[i].last_letter=code[i].letter[code[i].len-1];
code[i].bj=0;
}
sort(code,code+n,cmp);
int fir=0;
for(int i=1;i<n;i++){
if(code[i].first_letter!=code[i-1].first_letter){
pos[code[i-1].first_letter-97].endd=i-1;
pos[code[i-1].first_letter-97].star=fir;
fir=i;
}
}
pos[code[n-1].first_letter-97].endd=n-1;
pos[code[n-1].first_letter-97].star=fir;
for(int i=0;i<n;i++){
code[i].bj=1;
sum=1;
Dfs(i);
code[i].bj=0;
sum=0;
if(flag){
break;
}
}
if(flag==1){
cout<<"Ordering is possible."<<endl;
}
else{
cout<<"The door cannot be opened."<<endl;
}
}
return 0;
}
3)
Description
There is a large number of magnetic plates on every door. Every plate has one word written on it. The plates must be arranged into a sequence in such a way that every word begins with the same letter as the previous word ends. For example, the word ``acm'' can be followed by the word ``motorola''. Your task is to write a computer program that will read the list of words and determine whether it is possible to arrange all of the plates in a sequence (according to the given rule) and consequently to open the door.
Input
Output
If there exists such an ordering of plates, your program should print the sentence "Ordering is possible.". Otherwise, output the sentence "The door cannot be opened.".
Sample Input
3 2 acm ibm 3 acm malform mouse 2 ok ok
Sample Output
The door cannot be opened. Ordering is possible. The door cannot be opened.