P1330 封锁阳光大学+P8604 [蓝桥杯 2013 国 C] 危险系数+P3916 图的遍历

P1330 封锁阳光大学

题目描述

曹是一只爱刷街的老曹,暑假期间,他每天都欢快地在阳光大学的校园里刷街。河蟹看到欢快的曹,感到不爽。河蟹决定封锁阳光大学,不让曹刷街。

阳光大学的校园是一张由 n个点构成的无向图,n个点之间由 m 条道路连接。每只河蟹可以对一个点进行封锁,当某个点被封锁后,与这个点相连的道路就被封锁了,曹就无法在这些道路上刷街了。非常悲剧的一点是,河蟹是一种不和谐的生物,当两只河蟹封锁了相邻的两个点时,他们会发生冲突。

询问:最少需要多少只河蟹,可以封锁所有道路并且不发生冲突。

输入格式

第一行两个正整数,表示节点数和边数。 接下来 m 行,每行两个整数 u,v,表示点 u 到点 v之间有道路相连。

输出格式

仅一行如果河蟹无法封锁所有道路,则输出 Impossible,否则输出一个整数,表示最少需要多少只河蟹。

输入输出样例

输入 #1复制

3 3
1 2
1 3
2 3

输出 #1复制

Impossible

输入 #2复制

3 2
1 2
2 3

输出 #2复制

1

说明/提示

1.注意这个可能是非连通图,每一个点都需要搜索

2.相当于一个染色问题,用1黑色代表该点有河蟹,0白色代表没有河蟹,要求同一条边上的两个点颜色不能相同 

3.分别用1和0染色搜索的话需要注意每搜索一次,需要对是否染过色复位

4.Impossible是大写的

#include<bits/stdc++.h>
using namespace std;
vector<int> graph[10003];
int color[10003];
int reset[10003];
int n, m;
bool flag;//判断有没有答案
int dfs(int i, int col) {
	if (!flag) return 0;
	int sum = col;//sum计算的是染1的点的个数,如果开始染的是1这个颜色,直接计数为1,如果开始染的是0这个颜色的话就计数为0
	color[i] = col;
	for (int j = 0; j < graph[i].size(); j++) {
		int v = graph[i][j];
		if (color[v] == -1) {//该点没有被染过
			color[v] = (!col);//染上相反的颜色(0,1)
			sum += dfs(v, !col);
		}
		else if (color[v] == col)//该点颜色与相邻的点颜色相同
			flag = false;
	}
	return sum;
}
int main() {
	cin >> n >> m;
	for (int i = 0; i < m; i++) {
		int u, v;
		cin >> u >> v;
		graph[u].push_back(v);
		graph[v].push_back(u);
	}
	memset(color, -1, sizeof(color));
	flag = true;
	int ans = 0;
	for (int i = 1; i <= n; i++) {//非连通图的话每个点都要搜一下
		if (color[i] == -1) {
			memcpy(reset, color, sizeof(color));//用reset数组记录color数组的值,为染色为1的搜索初始化用
			int a = dfs(i, 0);//染0这个颜色搜索一遍
			memcpy(color, reset, sizeof(color));//初始化
			int b = dfs(i, 1);//染1这个颜色搜索一遍
			ans += min(a, b);//取最小值

		}
		if (!flag) break;
	}
	if (!flag) cout << "Impossible" << endl;	
	else 	cout << ans << endl;
	return 0;
}

其他参考

题解 P1330 【封锁阳光大学】 - KesdiaelKen 的博客 - 洛谷博客 (luogu.com.cn) 

上面这个会用到链式存图 

链式前向星——最完美图解 - 腾讯云开发者社区-腾讯云 (tencent.com)

题解 P1330 【封锁阳光大学】 - EricWay1024 - 洛谷博客 (luogu.com.cn)

P8604 [蓝桥杯 2013 国 C] 危险系数

题目背景

抗日战争时期,冀中平原的地道战曾发挥重要作用。

题目描述

地道的多个站点间有通道连接,形成了庞大的网络。但也有隐患,当敌人发现了某个站点后,其它站点间可能因此会失去联系。

我们来定义一个危险系数 DF(x,y)DF(x,y):

对于两个站点 xx 和 y(x\neq y),y(x=y), 如果能找到一个站点 zz,当 zz 被敌人破坏后,xx 和 yy 不连通,那么我们称 zz 为关于 x,yx,y 的关键点。相应的,对于任意一对站点 xx 和 yy,危险系数 DF(x,y)DF(x,y) 就表示为这两点之间的关键点个数。

本题的任务是:已知网络结构,求两站点之间的危险系数。

输入格式

输入数据第一行包含 22 个整数 n(2 \le n \le 1000)n(2≤n≤1000),m(0 \le m \le 2000)m(0≤m≤2000),分别代表站点数,通道数。

接下来 mm 行,每行两个整数 u,v(1 \le u,v \le n,u\neq v)u,v(1≤u,v≤n,u=v) 代表一条通道。

最后 11 行,两个数 u,vu,v,代表询问两点之间的危险系数 DF(u,v)DF(u,v)。

 

输出格式

一个整数,如果询问的两点不连通则输出 -1−1。

输入输出样例

输入 #1复制

7 6
1 3
2 3
3 4
3 5
4 5
5 6
1 6

输出 #1复制

2

说明/提示

时限 1 秒, 64M。蓝桥杯 2013 年第四届国赛

z是x和y的关键点说明从x到y的每一条路径都要经过z。

则只需要计算从x走到y一共有多少条路径,每找到一条路径就让这条路径上每个点访问次数加1。

若某点z被访问次数等于x和y之间的路径总数,那么该点就是关键点。(因为从x到y的每一条路径都要访问z点)

#include<bits/stdc++.h>
using namespace std;
int n, m;
int visit[1003];
vector <int> g[1003];
int bg, ed,num,cnt[1003],ans;
void dfs(int pos) {
	if (pos == ed) {
		num++;//路径数目加一
		for (int i = 1; i <= n; i++) {
			if (visit[i])cnt[i]++;//访问次数加1
		}
		return;
	}
	for (int i = 0; i < g[pos].size(); i++) {
		int next = g[pos][i];
		if (!visit[next]) {
			visit[next] = 1;
			dfs(next);
			visit[next] = 0;//复原重新搜索
		}
	}
	return;
}
int main() {
	cin >> n >> m;
	for (int i = 0; i <m; i++) {
		int x, y;
		cin >> x >> y;
		g[y].push_back(x);
		g[x].push_back(y);
	}
	cin >> bg >> ed;
	visit[bg] = 1;//标记起点访问过
	dfs(bg);
	for (int i = 1; i <= n; i++) {
		if (cnt[i] == num)
			ans++;
	}
	ans -= 2;//不包括起点和终点
	cout << ans;
	return 0;
}

P3916 图的遍历 

题目描述

给出 N 个点,M 条边的有向图,对于每个点 v,求 A(v) 表示从点 v出发,能到达的编号最大的点。

输入格式

第 1行 2 个整数 N,M表示点数和边数。

接下来 M 行,每行 2 个整数 U_i,V_i,表示边 (Ui​,Vi​)。点用 1,2,…,N 编号。

输出格式

一行 N 个整数 A(1),A(2),…,A(N)。

输入输出样例

输入 #1复制

4 3
1 2
2 4
4 3

输出 #1复制

4 4 3 4

说明/提示

 若要找该点能到达的最大编号点,那么只用看最大编号点的起点集合里面有没有该点就可以了。从最大点开始搜索。

#include<bits/stdc++.h>
using namespace std;
int n, m,ans[100003];
vector <int> g[100003];
void dfs(int st, int ed) {//st:起始点;ed:终点
	if (ans[st] > 0)return;//访问过 ,说明该点能够到达编号更大的点
	ans[st] = ed;//将ed作为st能够到达的点(即标记为已访问)
	for (int i = 0; i < g[st].size(); i++) {
		dfs(g[st][i], ed);//依次找以ed为终点的起点g[st][i],并赋值标记为已访问
	}
}
int main() {
	cin >> n >> m;
	for (int i = 0; i < m; i++) {
		int x, y;
		cin >> x >> y;
		g[y].push_back(x);
	}
	for (int i = n; i > 0; i--) {//从最大的点开始搜索
		dfs(i, i);//点i肯定是能够以它自身作为终点的
	}
	for (int i = 1; i <= n; i++)cout << ans[i] << ' ';
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值