洛谷P5663 加工零件题解

题目链接

题意

一个有 n n n 个点, m m m 条边的无向图,一个点想要得到一个 L L L 阶段的数,则与它有直接连边的点都要得到一个 L − 1 L-1 L1 阶段的数,如果一个点要得到一个第一阶段的数,则与它有直接连边的点需要给它提供一个原材料。

求: 有 T T T 组数据,每组数据输入一个 a a a L L L ,计算点 a a a 需要一个 L L L 阶段的数,点 1 是否需要提供原材料。

题解

如果将从 1 1 1 ~ a a a最短路径看作是 c n t cnt cnt 的话,则可以进行分类讨论:

分类一: L = c n t L = cnt L=cnt

不难发现,在这种情况下,轮到 1 时正好需要提供一个原材料

分类二: L > c n t L > cnt L>cnt ,在这一分类下情况相对复杂

第一种情况: L L L 是偶数

在这种情况下又可以进行分类,第一种是 c n t cnt cnt 为奇数,第二种是 c n t cnt cnt 为偶数。

观察上图不难发现:

c n t cnt cnt L L L 均为偶数时,1 需要提供一个原材料

c n t cnt cnt 为奇数, L L L 为偶数时,1 不需要提供原材料

第二种情况: L L L 为奇数

在这种情况下依旧需要分类,第一种是 c n t cnt cnt 为奇数,第二种是 c n t cnt cnt 为偶数。观察上图可知:

c n t cnt cnt L L L 均为奇数时,1 需要提供一个原材料

c n t cnt cnt 为偶数,而 L L L 为奇数时,1 不需要提供原材料

分类三: L < c n t L < cnt L<cnt

很容易看出, 在这种情况下,还没有到1就已经有点提供了原材料,1 不需要提供原材料

(关于其它点是否也需要提供原材料暂不考虑,因为题目只要求 1 是否要提供原材料)

将以上三种分类进行一定的整合后,我们可以得到三条结论:

  • L = c n t L = cnt L=cnt 时,必定需要提供原材料
  • L > c n t L > cnt L>cnt 时,如果 c n t cnt cnt L L L 的奇偶性相同,需要提供原材料;反之,则无需提供原材料
  • L < c n t L < cnt L<cnt 时,必定无需提供原材料

得到这三条结论之后,解决这道题的思路就非常明显了:

只要求出从 1 到 a a a 的最短路径,判断它与 L L L 的大小关系奇偶关系,根据上述结论判断是否需要提供原材料。

定义两个数组 o d d odd odd e v e n even even

o d d [ i ] odd[i] odd[i] 表示从 1 到 i i i 的奇数(步数)最短路径

e v e n [ i ] even[i] even[i] 表示从 1 到 i i i 的偶数最短路径

注意:需要将这两个数组都初始化成 INF,之后便于判断。

接下来根据题目意思正常建图,通过广度优先搜索得到从 1 到 a a a 的奇数最短路径和偶数最短路径(其中一定有一个值为INF),接下来进行判断:

L L L 为奇数时:

如果 o d d [ i ] odd[i] odd[i] <= L L L (如果 o d d [ i ] > L odd[i] > L odd[i]>L ,则 说明不存在奇数最短路径,或者此最短路径大于 L L L ,根据结论3必定不需要提供原材料), 需要提供原材料,否则不需要提供原材料。

L L L 为偶数时:

如果 e v e n [ i ] even[i] even[i] <= L L L (如果 e v e n [ i ] > L even[i] > L even[i]>L ,则 说明不存在偶数最短路径,或者此最短路径大于 L L L ,根据结论3必定不需要提供原材料), 需要提供原材料,否则不需要提供原材料。

代码实现:

#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
int even[100005];
int odd[100005];
vector<int> g[100005];
struct Node{
	int x;
	int step;
};
queue<Node> q;
int main() {
	memset(even, INF, sizeof(even));
	memset(odd, INF, sizeof(odd));
	int n, m, T;
	cin >> n >> m >> T;
	for (int i = 1; i <= m; i ++) {
		int x, y;
		cin >> x >> y;
		g[x].push_back(y);
		g[y].push_back(x);
	}
	
	q.push({1, 0});
	while (!q.empty()) {
		Node cur = q.front();
		q.pop();
		int x = cur.x;
		int step = cur.step;
		for (auto i : g[x]) {
			if (step % 2 == 0 and odd[i] == INF) { 
       //step 并非表示到点 x 的步数,step + 1才表示,在判断时需要转化一下
				q.push({i, step + 1});
				odd[i] = step + 1;
			}
			if (step % 2 == 1 and even[i] == INF) {
				q.push({i, step + 1});
				even[i] = step + 1;
			}
		}
	}
	while (T --) {
		int a, l;
		cin >> a >> l;
		if (l & 1) odd[a] <= l ? cout << "Yes" << endl : cout << "No" << endl;
		else even[a] <= l ? cout << "Yes" << endl : cout << "No" << endl;
	}
      //使用三目运算符,能更方便地完成判断
	return 0;
}
  • 24
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值