ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

ACM-ICPC寒假算法训练4:图论1(图的遍历)

2021-06-08 20:06:20  阅读:269  来源: 互联网

标签:遍历 int ++ ACM ICPC vis maxn dfs include


题1:封锁阳光大学:洛谷1330

算法分析:

首先这个图,可能不连通,我们针对其中一个联通分量来看:如果我们在一个点放下了一只河蟹,那么那个点与之邻接的所有点都不可以再放河蟹。那么对于一个点,我们就有两个策略:放河蟹与不放河蟹。这也就对应了一个节点的两个状态:0(不放)或者1(放)。我们可以以这个联通分量的任意一个点作为深度搜索的起点,然后进行深度遍历,我们遍历的目的其实只是为了检测在图被 0/1点铺满的时候,会不会有任意一条边的两个点都是 0的情况,如果有这种情况,那么就是Impossible了!因为这样就有一条边未被封锁。
那如果可以被封锁,我们怎么计算最少需要放多少个河蟹呢?对于一个联通子图,我们需要统计放 1的点的个数和放 0的点的个数的最小值,因为放 1的点和 0的点其实状态可以互换,所有我们选择其中的最小值即可。

AC代码:

#include <cstdio>
#include <vector>
using namespace std;

const int maxn = 1e4 + 5;
vector<int> G[maxn];
int n, m, a, b, ans, vis[maxn], cnt[2], col[maxn];// col[] 数组存下每个点的状态,以便检查合法性 
inline int Min(int x, int y) { return x < y ? x : y; } 
bool dfs(int p, int c){
	if(vis[p]){
		if(c == col[p])
			return true;
		else
			return false;	 
	}// 新遍历的点已经被访问过了,最后一步合法说明这个联通分量合法 
	vis[p] = 1, col[p] = c, cnt[c]++;
	for(int i = 0;i < (int)G[p].size();++i)
		if(!dfs(G[p][i], c ^ 1))
			return false;
	return true;
}

int main(){
	scanf("%d %d", &n, &m);
	while(m--){
		scanf("%d %d", &a, &b);
		G[a].push_back(b);
		G[b].push_back(a);
	}
	for(int i = 1;i <= n;++i){
		cnt[0] = cnt[1] = 0;
		if(!vis[i]){
			if(!dfs(i, 0)){
				printf("Impossible");
				return 0;
			}
			else
				ans += Min(cnt[0], cnt[1]);
		}
	}
	printf("%d", ans);
	return 0;
} 

题2:图的遍历:洛谷3916

算法分析:

这一题乍一看感觉这数据规模暴力搜DFS貌似过不去啊???
对 1-n 的每一个点求这个点能到达的编号最大的点,这个感觉会有很多无用的搜索啊。我们试着把样例画一下,发现过程如下:
1 -> 2 -> 4 ->3
于是输出: 4 4 3 4
那么我们不如反过来,反向建边,从大的编号节点开始深度遍历,因为与较大节点能够可达的节点的结果一定是这个较大节点,如果不能与较大节点可达,那么它的值一定就小于较大节点!

AC代码:

#include <cstdio>
#include <vector>
using namespace std;

const int maxn = 1e5 + 5;
vector<int> G[maxn];
int n, m, a, b, ans[maxn], vis[maxn];
void dfs(int p, int k){
	if(vis[p])	return ;
	vis[p] = 1;
	ans[p] = k;
	for(int i = 0;i < (int)G[p].size();++i)
		if(!vis[G[p][i]])
			dfs(G[p][i], k);
}
int main(){
	scanf("%d %d", &n, &m);
	while(m--){
		scanf("%d %d", &a, &b);
		G[b].push_back(a);// 反向建边
	}
	for(int i = n;i >= 1;--i)
		if(!vis[i])
			dfs(i, i); //新的联通链,最大的编号就是初始编号
	for(int i = 1;i <= n;++i)
		printf("%d ", ans[i]);
	return 0;
}

题3:基础题:查找文献 洛谷5318

算法分析:

这就不分析了,跑一遍dfs和一遍bfs就完事了

AC代码:

#include <cstdio>
#include <vector>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;

const int maxn = 1e5 + 5;
vector<int> G[maxn];
int n, m, a, b, vis[maxn];
void dfs(int p){
	if(vis[p])	return ;
	vis[p] = 1;
	printf("%d ", p);
	for(int i = 0;i < (int)G[p].size();++i)
		if(!vis[G[p][i]])
			dfs(G[p][i]);
}

void bfs(int p){
	memset(vis, 0, sizeof(vis));
	queue<int> q;
	q.push(p);
	vis[p] = 1;
	while(!q.empty()){
		int Now = q.front(); q.pop();
		printf("%d ", Now);
		for(int i = 0;i < (int)G[Now].size();++i)
			if(!vis[G[Now][i]])
			{
				vis[G[Now][i]] = 1;
				q.push(G[Now][i]);
			}
	}
} 
int main(){
	scanf("%d %d", &n, &m);
	while(m--){
		scanf("%d %d", &a, &b);
		G[a].push_back(b);
	}
	for(int i = 1;i <= n;++i)
		sort(G[i].begin(), G[i].end());
	dfs(1);
	printf("\n");
	bfs(1);
	return 0;
}

题4:无序字母对 洛谷1341

算法分析:

这题太坑了!!!
这题可以这样抽象建模:
单词:边(字符就是端点)
结果字符串:就是一个路径
需要寻找每个单词都出现的字符串:找到每条边都走到的路径(欧拉路径)

欧拉路径的算法分析:

欧拉路本质就是一个dfs,存在的条件是:
条件1:入度为奇数的点有0个或2个(0个是欧拉回路)
条件2:它一定要是一个联通图(联通性用并查集或者dfs)

注意事项:倒着存储点。

坑点分析:

1:自环用dfs不怕

2:老是RE(这个真的有点玄学……)

AC代码:

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

const int maxn = 3005;
int m, vis[maxn], deg[maxn], G[maxn][maxn], isOK, s, cnt, ans[maxn];
char a, b; 
void dfs(int p){
	if(vis[p])	return ;
	vis[p] = 1;
	for(int i = 0;i < maxn;++i)
		if(!vis[i] && G[p][i])
			dfs(i);
}

void euler(int p){
	for(int i = 0;i < maxn;++i){
		if(G[p][i]){
			G[p][i]--;
			G[i][p]--;
			euler(i);
		}
	}
	ans[cnt++] = p;
	return ;
}

int main(){
	cin >> m;
	for(int i = 0;i < m;++i){
		cin >> a >> b;
		G[a - 'A'][b - 'A']++;
		G[b - 'A'][a - 'A']++;
		deg[a - 'A']++;
		deg[b - 'A']++;
	}
	if(1 == m){
		if(a > b)
			cout << b << a;
		else
			cout << a << b;
		return 0;
	}
	for(int i = 0;i < maxn;++i)
		if(deg[i] & 1)
		{
			isOK++;
			if(!s)
				s = i;
		}
	if(0 != isOK && 2 != isOK){
		dfs(s);
		for(int i = 0;i < maxn;++i)
			if(!vis[i] && deg[i])
			{
				isOK = 1;
				break;
			}
	}
	if(0 != isOK && 2 != isOK)
		cout << "No Solution";
	else{
		euler(s);
		for(int i = cnt - 1;i >= 0;--i)
			cout << char(ans[i] + 65);
	}
	return 0;
}
// IcCvXggwPpl

标签:遍历,int,++,ACM,ICPC,vis,maxn,dfs,include
来源: https://blog.51cto.com/u_15262702/2883320

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有