------------------------------poj1386和poj2513是同一个类型
POJ1836:
视为求有向的欧拉通路(并非欧拉回路)
判别方法
①所有的点入度==出度或者②只有有两个入度减出度为1和-1的两个节点(终点,开点)其他点入度==出度则存在欧拉通路
由于这题需要用完全部的单词,所以要判断图是否全部连通,即用并查集即可判断有多少个集合。
代码:
/*
将一个单词的首位和末位视为一个顶点,则中间的单词则是一条边.
那么只需要记录下首位的出度和末位的入度,数据量就变成了26个.
用used[]记录下这个字母是否出现
*/
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <math.h>
#include <vector>
#include <cstdio>
#include <string>
#include<string.h>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
bool used[26];
int f[26];
int indegree[26];
int outdegree[26];
inline void read(int &m)//int
{
int x=0,f=1;char ch=getchar();//int
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
m=x*f;
}
int find(int x)
{
if(f[x]!=x)
f[x]=find(f[x]);
return f[x];
}
bool concect()
{
int cnt=0;
for(int i=0;i<26;i++)
if(used[i]&&f[i]==i)
cnt++;
return cnt==1;
}
int main()
{
int cas,n;
read(cas);
while(cas--)
{
read(n);
memset(used,0,sizeof(used));
memset(indegree,0,sizeof(indegree));
memset(outdegree,0,sizeof(outdegree));
for(int i=0;i<26;i++)
f[i]=i;
char ch[1010];
while(n--)
{
scanf("%s",ch);
int a=ch[0]-'a';
int b=ch[strlen(ch)-1]-'a';
outdegree[a]++;
indegree[b]++;
used[a]=used[b]=1;
if(find(a)!=find(b)) //Union
f[find(a)]=find(b);
}
int one=0;//出度比入度多一
int done=0;//入度比出度多一
bool flag=1;
for(int i=0;i<26;i++)
{
if(!used[i])continue;
if(indegree[i]-outdegree[i]>=2||outdegree[i]-indegree[i]>=2) //并不相等且太离谱了了- -
{
flag=0;
break;
}
if(indegree[i]-outdegree[i]==1&&++done>=2) //检查相差几个单位
{
flag=0;
break;
}
if(outdegree[i]-indegree[i]==1&&++one>=2)
{
flag=0;
break;
}
}
if(one!=done) flag=0;
if(!concect()) flag=0; //是否连通
if(flag)
printf("Ordering is possible.\n");
else
printf("The door cannot be opened.\n");
}
return 0;
}
poj2513
视为求无向图欧拉通路
无向图存在欧拉路的充要条件为:
① 图是连通的;
② 所有节点的度为偶数,或者有且只有两个度为奇数的节点。
由于这道题不能用map映射string的id,所以只能用字典树来映射
当然由于还要用完全部的火柴,也需要判断是否全部连通--并查集
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <math.h>
#include <vector>
#include <cstdio>
#include <string>
#include<string.h>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn=510010;
int f[maxn];
int degree[maxn];
int color=0;
struct trie
{
bool flag;
int id;
trie *next[26];
trie()
{
for(int i=0;i<26;i++)
next[i]=NULL;
flag=0;
id=0;
}
};
int insert_get(trie *root,char *ch)
{
trie *p=root;
int len=strlen(ch);
for(int i=0;i<len;i++)
{
if(p->next[ch[i]-'a']==NULL)
{
trie *temp=new trie;
p->next[ch[i]-'a']=temp;
}
p=p->next[ch[i]-'a'];
}
if(!p->flag)
{
p->flag=1;
p->id=++color;
}
return p->id;
}
int find(int x)
{
if(f[x]!=x)
f[x]=find(f[x]);
return f[x];
}
int connect()
{
int cnt=0;
for(int i=1;i<=color;i++)
if(f[i]==i)
cnt++;
return cnt==1;
}
int main()
{
trie *root=new trie;
for(int i=0;i<maxn;i++)
f[i]=i;
memset(degree,0,sizeof(degree));
char st1[20],st2[20];
bool ok=1; //这道题有空数据,在空数组这wa一次。特别标志一下
while(~scanf("%s %s",st1,st2))
{
ok=0;
int a=insert_get(root,st1);
int b=insert_get(root,st2);
degree[a]++;
degree[b]++;
if(find(a)!=find(b))
f[find(a)]=find(b);
}
int one=0;//度为奇数的节点个数
bool flag=1;
for(int i=1;i<=color;i++)
if(degree[i]%2==1)
one++;
if(ok||(connect()&&(one==0||one==2)))
printf("Possible\n");
else
printf("Impossible\n");
return 0;
}