欧拉回路:终点就是起点
一、无向图
1 存在欧拉路径的充要条件 : 度数为奇数的点只能有0或2个
2 存在欧拉回路的充要条件 : 度数为奇数的点只能有0个
二、有向图
1 存在欧拉路径的充要条件 : 要么所有点的 出度=入度;
要么除了两个点之外,其余所有点的 出度=入度
剩余的两个点:一个满足出度-入度=1(起点) 一个满足入度-出度=1(终点)
2 存在欧拉回路的充要条件 : 所有点的出度均等于入度
1 无向图
所有点的度数必须是奇数
所有边连通
2 有向图
所有点的入度等于出度
所有边连通
【模板】欧拉路径
这里要排序,最好用vector存入,因为按字典序最小排序,所以需要我们从小到大依次遍历
#include<bits/stdc++.h>
using namespace std;
const int N=1000010;
int e[N],ne[N],h[N],idx;
int din[N],dout[N],n,m;
int p[N],ans[N],cnt;
bool st[N];
bool used[N];
vector<int>f[N];
int del[N];
void dfs(int u)
{
for(int i=del[u];i<f[u].size();i=del[u])
{
del[u]=i+1;
dfs(f[u][i]);
}
ans[cnt++]=u;
}
int main()
{
cin>>n>>m;
memset(h,-1,sizeof h);
for(int i=1;i<=m;i++)
{
int a,b;
cin>>a>>b;
f[a].push_back(b);
dout[a]++;
din[b]++;
}
for(int i=1;i<=n;i++)
sort(f[i].begin(),f[i].end());//排序
bool flag=true;
int end=0,start=0;
int rep=1;
for(int i=1;i<=n;i++)
{
if(din[i]!=dout[i])
{
if(din[i]+1==dout[i]) {start++;rep=i;}
else if(dout[i]+1==din[i]) end++;
else {flag=false;break;}
}
}
//cout<<rep<<endl;
if(flag&&!(!start&&!end||start==1&&end==1)) flag=false;
// 判断起点终点数目是否正确
dfs(rep);//开始遍历
if(!flag){puts("No");return 0;}
for(int i=cnt-1;i>=0;i--) cout<<ans[i]<<" ";
return 0;
}
板子2
#include<bits/stdc++.h>
using namespace std;
const int N=4000010;
int e[N],ne[N],h[N],idx;
int din[N],dout[N],n,m;
int p[N],ans[N],cnt;
bool st[N];
bool used[N];
int del[N];
struct node
{
int a,b;
bool operator <(const node &W) const
{
if(a==W.a) return b<W.b;
return a<W.a;
}
}f[N];
void add(int a,int b)
{
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void dfs(int u)
{
for(int &i=h[u];~i;)
{
if(used[i])
{
i=ne[i];
continue;}
used[i]=1;
int j=e[i];
i=ne[i];
dfs(j);
}
ans[cnt++]=u;
}
int main()
{
cin>>n>>m;
memset(h,-1,sizeof h);
for(int i=1;i<=m;i++)
{
int a,b;
cin>>a>>b;
f[i]={a,b};
dout[a]++;
din[b]++;
}
sort(f+1,f+1+m);
for(int i=m;i>=1;i--)
{
add(f[i].a,f[i].b);
}
bool flag=true;
int end=0,start=0;
int rep=1;
for(int i=1;i<=n;i++)
{
if(din[i]!=dout[i])
{
if(din[i]+1==dout[i]) {start++;rep=i;}
else if(dout[i]+1==din[i]) end++;
else {flag=false;break;}
}
}
if((!flag)&&!(start==end&&start==1)) return !printf("No");
dfs(rep);
if(!flag){puts("NO");return 0;}
for(int i=cnt-1;i>=0;i--) cout<<ans[i]<<" ";
return 0;
}
P1127 词链
有向图判断+寻找欧拉路径
题目要求将所有的单词能否首位连接成一个句子。
明显的一笔画题目
单词的首尾字母可以看出顶点,单词看成一条边,题目要求最小字典序,先排序,在寻找路径
然后就是一些普通的有向图的判定了
#include<bits/stdc++.h>
using namespace std;
const int N=4000010;
int e[N],ne[N],w[N],h[N],idx;
int n,m,din[N],dout[N],cnt;
bool used[N];
int ans[N];
string str[1010];
int p[N];
bool st[N];
void add(int a,int b,int c)
{
e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;
}
void dfs(int u)
{
for(int &i=h[u];~i;)
{ int t=w[i];
if(used[i])
{
i=ne[i];
continue;
}
used[i]=true;
int j=e[i];
i=ne[i];
dfs(j);
ans[cnt++]=t;//里面存储边
}
//ans[cnt++]=u; //外面存储点
}
int find(int x)
{
if(x!=p[x]) p[x]=find(p[x]);
return p[x];
}
int main()
{
memset(h,-1,sizeof h);
cin>>n;
for(int i=1;i<=n;i++)
cin>>str[i];
sort(str+1,str+1+n);//题目要求最小字典序,先排序然后从大到小加边
for(int i=1;i<=26;i++) p[i]=i;//初始化并查集
for(int i=n;i>=1;i--)
{
int a=str[i][0]-'a'+1,b=str[i][str[i].size()-1]-'a'+1;
add(a,b,i);
din[b]++;
dout[a]++;
st[a]=st[b]=true;
p[find(a)]=find(b);//合并并查集
}
int flag=true;
int start=0,end=0;
int rep=str[1][0]-'a'+1;//字典序最小的作为起点
for(int i=1;i<=26;i++)//有向图的欧拉回路判断
{
if(din[i]!=dout[i])
{
if(din[i]+1==dout[i]) {start++;rep=i;}
else if(dout[i]+1==din[i]) {end++;}
else
{
flag=false;
break;
}
}
}
// 有向图的欧拉回路判断
if(flag&&!(!start&&!end||end==1&&start==1)) flag=false;
//判断整个图是否连通
int res=-1;
for(int i=1;i<=26;i++)
{
if(st[i])
{
if(res==-1) res=find(i);
else if(res!=find(i))
{
flag=false;
break;
}
}
}
dfs(rep);
if(!flag||cnt!=n)
{
puts("***");
return 0;
}
else
{
for(int i=cnt-1;i>=0;i--)
{
if(i!=0)
cout<<str[ans[i]]<<".";
else cout<<str[ans[i]];
}
}
return 0;
}
P1341 无序字母对
无向图+寻找欧拉路径;
寻找最小的,要求从最小的开始搜
题目范围比较小,可以用邻接矩阵存储建图。
无向图要求
1 存在欧拉路径的充要条件 : 度数为奇数的点只能有0或2个,若为2个,从小到大遍历找起点
2 存在欧拉回路的充要条件 : 度数为奇数的点只能有0个
#include<bits/stdc++.h>
using namespace std;
const int N=510;
int ans[1100];
int n,m;
int g[N][N];
int d[N],cnt;
void dfs(int u)
{
for(int i=1;i<=n;i++)
{
if(g[u][i])
{
g[u][i]--;
g[i][u]--;
dfs(i);
}
}
ans[cnt++]=u;
}
int main()
{
char s[3];
cin>>m;
int rep=100001;
for(int i=1;i<=m;i++)
{
cin>>s;
int a=s[0]-'A'+1,b=s[1]-'A'+1;
g[a][b]++;
g[b][a]++;
n=max(n,max(a,b));
rep=min(rep,min(a,b));
d[a]++;
d[b]++;
}
int start=rep;//若奇数度数为0,起点为最小的数
int res=0;
for(int i=1;i<=n;i++)
if(d[i]%2)//找起点
{
start=i;
break;
}
for(int i=1;i<=n;i++)
if(d[i]%2)
{
res++;
}
if (res != 0 && res != 2) {
printf("No Solution\n");
return 0;
}
dfs(start);
if(cnt<m+1) puts("No Solution");
else
for(int i=cnt-1;i>=0;i--)
printf("%c",ans[i]+'A'-1);
return 0;
}
P3520 [POI2011] SMI-Garbage
如果是输出简单环(环中每个点除了起点其他每个点只经过一次)
欧拉回路找环,可以用栈存储。
#include<bits/stdc++.h>
using namespace std;
const int N=2000010;
int e[N],ne[N],h[N],w[N];
int d[N];
int ans[N],cnt,idx;
bool used[N];
int sum,n,m;
void add(int a,int b)
{
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void dfs(int u,int root)
{
ans[++cnt]=u;
d[u]-=2;//进一次,出一次
for(int &i=h[u];~i;)
{
if(used[i])
{
i=ne[i];
continue;
}
used[i]=used[i^1]=true;
int j=e[i];
i=ne[i];
if(u!=root&&j==root) //如果形成环了
{
ans[++cnt]=j;
sum++;
return;
}
dfs(j,root);
return;
}
}
int main()
{
memset(h,-1,sizeof h);
cin>>n>>m;
for(int i=1;i<=m;i++)
{
int a,b,s,t;
cin>>a>>b>>s>>t;
if(s==t)continue;//不需要改变颜色的不建边
add(a,b);
add(b,a);
d[a]++;
d[b]++;
}
for(int i=1;i<=n;i++)
{
if(d[i]%2)
{
puts("NIE");
return 0;
}
}
for(int i=1;i<=n;i++)
{
if(!d[i]) continue;
while(d[i])
{
dfs(i,i);
}
}
int i=1;
printf("%d\n",sum);
while(i<=cnt)
{
int root=ans[i],k=1;
i++;
while(root!=ans[i]&&i<=cnt)
i++,k++;
printf("%d ",k);
for(int j=i-k;j<=i;j++)
printf("%d ",ans[j]);
printf("\n");
i++;
}
return 0;
}
自己动手的题目
P1333 瑞瑞的木棍
很裸的题目
对输入的颜色编号就行
#include<bits/stdc++.h>
using namespace std;
const int N=2000010;
unordered_map<string,int>mp;
string s[N];
int d[N];
int e[N],ne[N],h[N],idx;
int n,m;
int din[N],dout[N],ans[N],cnt;
bool used[N];
void add(int a,int b)
{
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void dfs(int u)
{
for(int &i=h[u];~i;)
{
if(used[i])
{
i=ne[i];
continue;
}
int j=e[i];
used[i]=true;
i=ne[i];
dfs(j);
}
ans[++cnt]=u;
}
int main()
{
int sum=1;
memset(h,-1,sizeof h);
char s1[20],s2[20];
while(~scanf("%s%s",s1,s2))
{
m++;
if(!mp[s1])
mp[s1]=sum++;
if(!mp[s2])
mp[s2]=sum++;
add(mp[s1],mp[s2]);
din[mp[s2]]++;
dout[mp[s1]]++;
}
int flag=true;
int S=0,T=0,rep=1;
for(int i=1;i<sum;i++)
{
if(din[i]+dout[i]&1)
{
if(din[i]+1==dout[i]) {S++;rep=i;}
else if(dout[i]+1==din[i]) T++;
else {
flag=false;
break;
}
}
}
if(flag&&!(!S&&!T||S==1&&T==1)) flag=false;
dfs(rep);
if(!flag||cnt!=m+1) puts("Impossible");
else
puts("Possible");
return 0;
}