洛谷P5663 加工零件(NOIp2019普及组T4)+对Dijkstra和SPFA的理解加深

题目描述

凯凯的工厂正在有条不紊地生产一种神奇的零件,神奇的零件的生产过程自然也很神奇。工厂里有 \(n\) 位工人,工人们从 \(1 \sim n\) 编号。某些工人之间存在双向的零件传送带。保证每两名工人之间最多只存在一条传送带。

如果 \(x\) 号工人想生产一个被加工到第 \(L (L \gt 1)\) 阶段的零件,则所有与 \(x\) 号工人有传送带直接相连的工人,都需要生产一个被加工到第 \(L - 1\) 阶段的零件(但 \(x\) 号工人自己无需生产第 \(L - 1\) 阶段的零件)。

如果 \(x\) 号工人想生产一个被加工到第 1 阶段的零件,则所有与 \(x\) 号工人有传送带直接相连的工人,都需要为 \(x\) 号工人提供一个原材料。

轩轩是 1 号工人。现在给出 \(q\) 张工单,第 \(i\) 张工单表示编号为 \(a_i\) 的工人想生产一个第 \(L_i\) 阶段的零件。轩轩想知道对于每张工单,他是否需要给别人提供原材料。他知道聪明的你一定可以帮他计算出来!

输入格式

第一行三个正整数 \(n\)\(m\)\(q\),分别表示工人的数目、传送带的数目和工单的数目。

接下来 \(m\) 行,每行两个正整数 \(u\)\(v\),表示编号为 \(u\)\(v\) 的工人之间存在一条零件传输带。保证 \(u \neq v\)

接下来 \(q\) 行,每行两个正整数 \(a\)\(L\),表示编号为 \(a\) 的工人想生产一个第 \(L\) 阶段的零件。

输出格式

\(q\) 行,每行一个字符串 \(Yes\) 或者 \(No\)。如果按照第 \(i\) 张工单生产,需要编号为 1 的轩轩提供原材料,则在第 \(i\) 行输出 \(Yes\);否则在第 \(i\) 行输出 \(No\)。注意输出不含引号。

输入输出样例

输入 #1

3 2 6
1 2
2 3
1 1
2 1
3 1
1 2
2 2
3 2

输出 #1

No
Yes
No
Yes
No
Yes

输入 #2

5 5 5
1 2
2 3
3 4
4 5
1 5
1 1
1 2
1 3
1 4
1 5

输出 #2

No
Yes
No
Yes
Yes

说明/提示

【输入输出样例 1 说明】

编号为 1 的工人想生产第 1 阶段的零件,需要编号为 2 的工人提供原材料。

编号为 2 的工人想生产第 1 阶段的零件,需要编号为 1 和 3 的工人提供原材料。

编号为 3 的工人想生产第 1 阶段的零件,需要编号为 2 的工人提供原材料。

编号为 1 的工人想生产第 2 阶段的零件,需要编号为 2 的工人生产第 1 阶段的零 件,需要编号为 1 和 3 的工人提供原材料。

编号为 2 的工人想生产第 2 阶段的零件,需要编号为 1 和 3 的工人生产第 1 阶段的零件,他/她们都需要编号为 2 的工人提供原材料。

编号为 3 的工人想生产第 2 阶段的零件,需要编号为 2 的工人生产第 1 阶段的零件,需要编号为 1 和 3 的工人提供原材料。

【输入输出样例 2 说明】

编号为 1 的工人想生产第 1 阶段的零件,需要编号为 2 和 5 的工人提供原材料。

编号为 1 的工人想生产第 2 阶段的零件,需要编号为 2 和 5 的工人生产第 1 阶段的零件,需要编号为 1,3,4 的工人提供原材料。

编号为 1 的工人想生产第 3 阶段的零件,需要编号为 2 和 5 的工人生产第 2 阶段的零件,需要编号为 1,3,4 的工人生产第 1 阶段的零件,需要编号为 2,3,4,5 的工人提供原材料。

编号为 1 的工人想生产第 4 阶段的零件,需要编号为 2 和 5 的工人生产第 3 阶段的零件,需要编号为 1,3,4 的工人生产第 2 阶段的零件,需要编号为 2,3,4,5 的工人生产第 1 阶段的零件,需要全部工人提供原材料。

编号为 1 的工人想生产第 5 阶段的零件,需要编号为 2 和 5 的工人生产第 4 阶段的零件,需要编号为 1,3,4 的工人生产第 3 阶段的零件,需要编号为 2,3,4,5 的工人生产第 2 阶段的零件,需要全部工人生产第 1 阶段的零件,需要全部工人提供原材料。

【数据规模与约定】

共 20 个测试点。

\(1 \leq u, v, a \leq n\)

测试点 1~4,\(1 \leq n, m \leq 1000\)\(q = 3\)\(L = 1\)

测试点 5~8,\(1 \leq n, m \leq 1000\)\(q = 3\)\(1 \leq L \leq 10\)

测试点 9~12,\(1 \leq n, m, L \leq 1000\)\(1 \leq q \leq 100\)

测试点 13~16,\(1 \leq n, m, L \leq 1000\)\(1 \leq q \leq 10^5\)

测试点 17~20,\(1 \leq n, m, q \leq 10^5\)\(1 \leq L \leq 10^9\)

思路

\(很容易可以想到没有环的情况,只需要判断奇偶性即可。为什么只需要判断奇偶性呢?我们可以想一下,假如说点 A 和点 B 之间有一条边相连,现在需要点 A 生产第7阶段的零件,那么 B 就要生产第6阶段的零件, A 又要生产第5阶段的零件,所以一条边走两遍就只需要判断奇偶性。因为每条边之间没有边权,我们可以假设都是1,这样就可以想到用BFS做。因为只需要判断路径的奇偶性,而且若路径长度过长(比如1号点跟3号点之间的距离是7,但是3号点只需要生产第3阶段的零件,这肯定就不行了)也不行,所以如果这两点之间的最短路过长就不满足条件了,不是最短路只要奇偶性一样,只要走两遍一条边就可以转换回去。所以只需要求出两点之间的奇最短路和偶最短路。由于我们只需要求出1号结点与其它结点的最短路,所以很明显是单源最短路。Dijkstra的原理是贪心,环的情况处理不了(我这个题用不行,可能是我菜QAQ)而SPFA的原理恰巧就是BFS。所以我们就可以用SPFA来求出1号结点与其它结点的奇最短路和偶最短路,再判断一下奇偶性就可以了。别忘记特判两点之间没有奇最短路或偶最短路的情况,这样就可以切掉这道题了。~~我居然用了3天时间才AK了去年的普及组的题目,还看了题解,我是垃圾。~~\)

代码

#include
#include
#include
#include
#include
#include
using namespace std;
const int N=100005,M=200005;
int edge[M],head[N],Next[M],ver[M],d[N];
bool v[N];
int t,n,m,tot;
int ou[N],ji[N];//汉语拼音QAQ
queue q;
void add(int x,int y){
	ver[++tot]=y;
	edge[tot]=1;
	Next[tot]=head[x];
	head[x]=tot;
}//存图
void SPFA(){
    for(int i=0;i<=N-4;i++){
    	ou[i]=10000000;
	}//为了便于判断有没有奇最短路或偶最短路,我存了10000000
    for(int i=0;i<=N-4;i++){
    	ji[i]=10000000;
	}
    memset(v,0,sizeof(v));
	ou[1]=0;
	v[1]=1;
    q.push(1);
    while(!q.empty()){
    	int x=q.front();
    	q.pop();
    	v[x]=0;
    	for(int i=head[x];i;i=Next[i]){
    		int y=ver[i],z=1;
    		if(ou[x]+zl){
	    		printf("No\n");
			}//如果最短路距离过长,那么肯定不行
			else{
				if(ou[a]==10000000){
					printf("No\n");//没有偶最短路
				}
				else{
					printf("Yes\n");
				}
			}
		}
		else{
			if(ji[a]>l){
				printf("No\n");
			}
			else{
				if(ji[a]==10000000){
					printf("No\n");
				}
				else{
					printf("Yes\n");
				}
			}
		}
	}
	return 0;
}

对Dijkstra和SPFA认识的加深

\(一开始这个题我是用Dijkstra来做的,但是处理不了有环的情况(也有可能是我写错了),只得了35pts。但是我只改了这一个地方,就是把Dijkstra改成SPFA,它就神奇的A了,这也不得不引起我对这两个最短路算法的加深思考:到底最大的差别是什么?其实它们之间的代码几乎完全一样,只有两个差别:1.Dijkstra用的是小根堆来存储最短路,而SPFA用的是队列。2.Dijkstra不会重复标记点,即这个点我走过之后不会重复走。但是SPFA可以再将出队的点入队,这也是Dijkstra不能处理负边权而SPFA可以的原因。回归证明的原理,Dijkstra的原理是贪心,而SPFA是广搜。由于边权都是正的,所以Dijkstra在与当前点相连的边中取最小的加入小根堆并更新最短路,那么这个最短路肯定不会再被更新。因为如果它被更新,那肯定是这两点之间存在一个断点,使得两点分别到断点的距离之和小于直接相连。但是由于我选的是最短的那条边,所以起点与断点相连的那条边不可能更短,也就是不可能更新最短路。但反之,如果存在负边权,是存在这种情况的,所以出队的元素可能要再入队一遍来更新最小值。对这个题而言,由于是小根堆,它每次都是找最近的那个点然后走,那肯定不能绕一圈回来更新自己,但是SPFA可以,所以不能用Dijkstra而应该用SPFA。\)

你可能感兴趣的:(洛谷P5663 加工零件(NOIp2019普及组T4)+对Dijkstra和SPFA的理解加深)