一本通 3.4.1 图的遍历

Posted xuqw11111

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一本通 3.4.1 图的遍历相关的知识,希望对你有一定的参考价值。

图的遍历

1341:【例题】一笔画问题

【题目描述】

如果一个图存在一笔画,则一笔画的路径叫做欧拉路,如果最后又回到起点,那这个路径叫做欧拉回路。

根据一笔画的两个定理,如果寻找欧拉回路,对任意一个点执行深度优先遍历;找欧拉路,则对一个奇点执行dfs,时间复杂度为O(m+n),m为边数,n是点数。

【题目分析】

 定理1:存在欧拉路的条件:图是连通的,有且只有2个奇点。
定理2:存在欧拉回路的条件:图是连通的,有0个奇点。

对每个点进行深搜,并对搜到的点进行计数count,如果count==n,则该路径为欧拉路 使用深搜可以记录路径并将路径倒着输出

【代码实现】

#include<bits/stdc++.h>
#define N 1005
using namespace std;
int f[N][N];
int s[N];
int b[N];
int n, m;
int t = 0;
void dfs(int z) 
	for (int i = 1; i <= n; i++) 
		if (f[z][i] == 1) 
			f[z][i] = f[i][z] = 0;
			dfs(i);
		
	
	b[t++] = z;

int main() 
	//定理1:存在欧拉路的条件:图是连通的,有且只有2个奇点。
	//定理2:存在欧拉回路的条件:图是连通的,有0个奇点。
	cin >> n >> m;
	for (int i = 1; i <= m; i++) 
		int x, y;
		cin >> x >> y;
		f[x][y] = f[y][x] = 1;
		s[x]++;
		s[y]++;
	
	int flag = 1;
	for (int i = 1; i <= n; i++)
		if (s[i] % 2 == 1)
			flag = i;
	dfs(flag);
	for (int i = 0; i < t; i++) 
		cout << b[i] << " ";
	
	return 0;

1374:铲雪车(snow)

【题目描述】

随着白天越来越短夜晚越来越长,我们不得不考虑铲雪问题了。整个城市所有的道路都是双车道,因为城市预算的削减,整个城市只有1辆铲雪车。铲雪车只能把它开过的地方(车道)的雪铲干净,无论哪儿有雪,铲雪车都得从停放的地方出发,游历整个城市的街道。现在的问题是:最少要花多少时间去铲掉所有道路上的雪呢?

【题目分析】

问题的本质:坐标点连接的欧拉回路
问题的求解:所有道路长度求和,并乘2得到总距离,根据速度求出时间,小时和分钟均为整数,分钟采用四舍五入

【代码实现】

#include <bits/stdc++.h>
using namespace std;


int main() 

	long long int x, y;

//	clock_t s = clock();

	cin >> x >> y;

	long long int x1, y1, x2, y2;
	double sum = 0;
	while (cin >> x1 >> y1 >> x2 >> y2) 
		sum += sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
	

	double ans = sum * 2 / 20 / 1000;
	long long int h = (int)ans;
	long long int m = (int)((ans - h) * 60 + 0.5);
	cout << h << ":" << setfill('0') << setw(2) << m;

//	cout << endl << "the time cost is:" << clock() - s  << endl;
	return 0;

1375:骑马修栅栏(fence)

【题目描述】

农民John每年有很多栅栏要修理。他总是骑着马穿过每一个栅栏并修复它破损的地方。

John是一个与其他农民一样懒的人。他讨厌骑马,因此从来不两次经过一个一个栅栏。你必须编一个程序,读入栅栏网络的描述,并计算出一条修栅栏的路径,使每个栅栏都恰好被经过一次。John能从任何一个顶点(即两个栅栏的交点)开始骑马,在任意一个顶点结束。

每一个栅栏连接两个顶点,顶点用1到500标号(虽然有的农场并没有500个顶点)。一个顶点上可连接任意多(≥1)个栅栏。所有栅栏都是连通的(也就是你可以从任意一个栅栏到达另外的所有栅栏)。

你的程序必须输出骑马的路径(用路上依次经过的顶点号码表示)。我们如果把输出的路径看成是一个500进制的数,那么当存在多组解的情况下,输出500进制表示法中最小的一个 (也就是输出第一个数较小的,如果还有多组解,输出第二个数较小的,等等)。 输入数据保证至少有一个解。

【题目分析】

特别的:两个顶点可能连接多个栅栏,节点的编号信息不确定

数据存储:读入每一条边,使用邻接矩阵进行记录,两点的连接信息记录边的数量,统计最大节点的标号n

搜索准备:确定起点start,从1到n枚举每个点的度,找到奇数度的点,如果没有从1开始找存在连接关系的最小节点

深度搜索:对起点start进行深度优先搜索,并将搜索节点信息存入数组,最后反向打印节点信息

【代码实现】

#include <bits/stdc++.h>
using namespace std;
#define N 2005

int edge[N][N];
int n, m;
int du[N];
int pos, circle[N];

void dfs(int cur) 
	circle[++pos] = cur;
	for (int i = 1; i <= n; i++) 
		if (edge[cur][i]) 
			edge[cur][i]--;
			edge[i][cur]--;
			dfs(i);
//			break;
		
	


int main() 
	//input data
	cin   >> m;
	for (int i = 1; i <= m; i++) 
		int x, y;
		cin >> x >> y;
		edge[x][y]++;
		edge[y][x]++;
		du[x]++, du[y]++;
		n = max(n, max(x, y));
	
//	clock_t s = clock();
	int start = 0;
	for (int i = 1; i <= n; i++)
		if (du[i] % 2 == 1) 
			start = i;
			break;
		
	if (start == 0) 
		for (int i = 1; i <= n; i++) 
			if (du[i]) 
				start = i;
				break;
			
		
	
	dfs(start);

	for (int i = 1; i <= pos; i++) 
		cout << circle[i] << endl;
	

	//output time
//	cout << endl << clock() - s << endl;
	return 0;

loj10151. 「一本通 5.1 练习 2」分离与合体

思路:
  注意初始化dp[i][i]=0,输出顺序时层序遍历。

#include<cstdio>
#include<iostream>
#include<string>
#include<queue>
using namespace std;
const int maxn = 310;
void qread(int &x){
    x = 0;
    register int ch = getchar();
    if(ch < 0 || ch > 9)    ch = getchar();
    while(ch >= 0 && ch <= 9)    x = 10 * x + ch - 48, ch = getchar();
}
int data[maxn];
int n;
int dp[maxn][maxn];
int sum[maxn];
int pos[maxn][maxn];
queue<pair<int, int> > qu;
void show(int l, int r){
    if(l == r)
        return ;
    cout << pos[l][r] << " ";
    qu.push(make_pair(l, pos[l][r]));
    qu.push(make_pair(pos[l][r] + 1, r));
    while(!qu.empty()){
        int a = qu.front().first, b = qu.front().second;
        qu.pop();
        show(a, b);
    }
}
int main(void){
    qread(n);
    for(int i = 1; i<=n; ++i){
        qread(data[i]);
        dp[i][i] = 0;
        sum[i] = sum[i-1] + data[i];
    }
    for(int L = 1; L < n; ++L)
        for(int i = 1; i + L <= n; ++i){
            int j = i + L;
            dp[i][j] = 0;
            for(int k = i; k < j; ++k){
                int t = dp[i][k] + dp[k + 1][j] + (data[i] + data[j]) * data[k];
                if(t > dp[i][j]){
                    dp[i][j] = t;
                    pos[i][j] = k;
                }
            }
        }
    printf("%d
", dp[1][n]);
    show(1, n);            
}

 


以上是关于一本通 3.4.1 图的遍历的主要内容,如果未能解决你的问题,请参考以下文章

图论广度优先搜索

一起玩转图论算法之二:图的深度优先遍历

图灵算法群《啊哈算法》领读

《图论》——广度优先遍历算法(BFS)

图论:图的四种最短路径算法

图论:图的四种最短路径算法