欧拉路径:欧拉路是指从图中任意一个点开始到图中任意一个点结束的路径,并且图中每条边通过的且只通过一次。
欧拉回路:欧拉回路是指起点和终点相同的欧拉路。
对于无向图,所有边都是连通的
(1)存在欧拉路径的充分必要条件:度数为奇数的点只能是0个或者2个
(2)存在欧拉回路的充分必要条件:度数为奇数的只能是0个
2对于有向图,所有边都是连通的
(1)存在欧拉路径的充分必要条件:要么所有点的出度均等于入度, 要么除了两个点之外,其余所有点的出度等于入度,剩余的两个点:一个满足出度比入度多1(起点),另一个满足入度比出度多1(终点)
(2)存在欧拉回路的充分必要条件:所有点的出度均等于入度
整个城市所有的道路都是双向车道
保证:铲雪车从起点一定可以到达任何街道。
第一行:该城市的所有道路的度都是2 (偶数的),那么这就符合欧拉路径
第二行:铲车从起点可以到达任何点,那么这个图是连通的
这个题就像 脑筋急转弯 一样, 求一下所有的边的路径长度之和,除以速度就是时间了
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
int main()
{
double x1, y1, x2, y2;
cin >> x1 >> y1;
double sum = 0;
while (cin >> x1 >> y1 >> x2 >> y2)
{
double dx = x1 - x2;
double dy = y1 - y2;
sum += sqrt(dx * dx + dy * dy) * 2;
}
int minutes = round(sum / 1000 / 20 * 60);
int hours = minutes / 60;
minutes %= 60;
printf("%d:%02d\n", hours, minutes);
return 0;
}
1
100000 3
30219 30219
68902 68902
83872 83872
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100010, M = 400010;
int type;
int n, m;
int h[N], ne[M], e[M], idx;
int ans[M], cnt;
bool used[M];
int din[N], dout[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;){//该行为引用,给 h[u] 起一个别的名字叫做 i
if(used[i]){
h[u] = ne[i];
continue;
}
used[i] = true;
if(type == 1) used[i ^ 1] = true;
int t;
if(type == 1){
t = i/2 + 1;
if(i & 1) t = -t;
}
else t = i + 1;
int j = e[i];
h[u] = ne[i];
dfs(j);
ans[cnt ++] = t;
}
}
int main(){
scanf("%d",&type);
scanf("%d%d",&n, &m);
memset(h, -1, sizeof h);
for(int i = 0;i < m;i ++){
int a, b;
scanf("%d%d", &a, &b);
add(a, b);
if(type == 1) add(b, a);
din[b] ++, dout[a] ++;
}
if(type == 1){
for(int i = 1;i <= n;i ++)
if((din[i] + dout[i]) & 1){
puts("NO");
return 0;
}
}
else{
for(int i = 1;i <= n;i ++)
if(din[i] != dout[i]){
puts("NO");
return 0;
}
}
for(int i = 1;i <= n ; i ++)
if(~h[i]){
dfs(i);
break;//如果是多个图的话,就无法找到欧拉路径
/*
1
100000 3
30219 30219
68902 68902
83872 83872
这个数据就是说有三个自环,是三个图,没有欧拉路径
而如果有 单点 的话是可以算作为有欧拉路径的,
给定一张图,请你找出欧拉回路,即在图中找一个环使得每条边都在环上出现恰好一次。
题中是这么说的,所以只要所有的边在一个环内就可以,可以有孤立的点
*/
}
if(cnt < m){
puts("NO");
return 0;
}
puts("YES");
for(int i = cnt - 1;i >= 0 ;i --)
printf("%d ",ans[i]);
puts("");
return 0;
}
我们如果把输出的路径看成是一个500进制的数,那么当存在多组解的情况下,输出500进制表示法中最小的一个 (也就是输出第一个数较小的,如果还有多组解,输出第二个数较小的,等等)。
这个题麻烦因为要字典序最小的输出
这是一个难点,因为存点(搜索)的时候,是逆序的,
而并不是逆序的最大值为顺序的最小值
比如 121 和 212
从这个数据就可以看出来,其实如果是 1 3 5 三个点的话,从 1 出发,然后遍历完 3 5 后还是得回到 1 的,因为这是欧拉回路,得回到起始点
那么这就简单了,按照从小到大进行搜索,最后返回的话也是从小到大的,那么就是 字典序最小 的。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 510, M = 1030;
int m;
int g[N][N];
int ans[M], cnt;
int d[N];//记录度数
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 ++] = i; //这么写也对
}
}
//ans[ cnt ++ ] = u; //这样写也对
}
int main(){
scanf("%d",&m);
while ( m -- ){
int a, b;
scanf("%d%d", &a, &b);
g[a][b] ++;
g[b][a] ++;
d[a] ++ ;
d[b] ++;
}
int root = 1;
while(!d[root]) root ++;
for(int i = 1;i < N;i ++){
if(d[i] & 1){
root = i;//寻找度数为奇数的点,如果存在,则从该点开始走
break;//要求起点为最小值
}
}
dfs(root);
ans[cnt ++] = root;
for(int i = cnt - 1; i >= 0;i --){
printf("%d\n",ans[i]);
}
return 0;
}
只要判断是否存在欧拉路径就行,不用找到路径是什么
根据欧拉路径的定义进行判断即可
存在的点在同一连通块内,
处起始点和终止点之外的所有点入度均等于出度,
起始点 入度 是 出度 +1,终止点 出度 是 入度+1
或者 起始点 和 终止点 的 入度 出度 相等
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 30;
int n;
int h[N];
int din[N], dout[N];
bool st[N];
int find(int u){
if(h[u] == u)
return h[u];
return h[u] = find(h[u]);
}
int main(){
int _;
char ch[1010];
scanf("%d",&_);
while(_--){
scanf("%d",&n);
memset(din, 0, sizeof din);
memset(dout, 0, sizeof dout);
memset(st, 0, sizeof st);
for(int i = 0;i < 26;i ++) h[i] = i;
for(int i = 0;i < n;i ++){
scanf("%s",&ch);
int a = ch[0] - 'a', b = ch[strlen(ch) - 1] - 'a';
st[a] = true, st[b] = true;
h[find(a)] = find(b);
din[b] ++, dout[a] ++;
}
bool success = true;
int s = 0, e = 0;
for(int i = 0;i < 26;i ++)
if(din[i] != dout[i]){
if(din[i] == dout[i] + 1)
e ++;
else if(din[i] + 1 == dout[i])
s ++;
else
success = false;
}
if(!(e == 1 && s == 1 || e == 0 && s == 0))
success = false;
int rep = -1;
for(int i = 0;i < 26;i ++)//在同一连通块内
if(st[i]){//找到有边的点
if(rep == -1)
rep = find(i);
else
if(rep != find(i)){
success = false;
break;
}
}
if(success == false)
puts("The door cannot be opened.");
else
puts("Ordering is possible.");
}
return 0;
}
对于这个题写了一个搜索,然后一直wa,说一下原因
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 200, M = 100010;
int m;
int g[200][200];
int din[N], dou[N];
int cnt;
char ch[1100];
void dfs(int u){
for(int i = 'a'; i <= 'z'; i ++)
if(g[u][i]){
g[u][i] --;
dfs(i);
cnt ++;
break;
}
}
int main(){
int l, a, b;
int _;
scanf("%d",&_);
while(_--){
memset(g, 0, sizeof g);
memset(din, 0, sizeof din);
memset(dou, 0, sizeof dou);
cnt = 0;
scanf("%d",&m);
for(int i = 0;i < m;i ++){
scanf("%s",&ch);
l = strlen(ch);
a = (int)ch[0];
b = (int)ch[l-1];
g[a][b] ++;
din[b] ++, dou [a] ++;
}
int root = 'a';
while(!dou[root] && !din[root])
root ++;
for(int j = 'a';j <= 'z'; j ++)
if(dou[j] == din[j] + 1){
root = j;
break;
}
dfs(root);
if(cnt == m){
printf("Ordering is possible.\n");
}
else
printf("The door cannot be opened.\n");
}
return 0;
}
/*
3
abc
cba
cba
*/
无论加不加break都是错的,
如图所示
加入break 的话,从 s 走到 e 的话,可能只走了红线的部分,而环的部分跳过,导致 cnt < m
如果不加入break可能导致这个红色边和黑色边都扫描了,cnt == m导致判断为是欧拉路径,其实不是的
所以先根据欧拉路径的条件判断是否是,然后再找路径是什么才是正确是做法
虽然说这个题并没有要求路径吧