3 条题解
-
0
这是一份标准的 AC 代码。
解题核心思路
-
奇偶性分析:在一个无向图中,如果存在一条从 到 长度为 的路径,那么可以通过在某条边上“反复横跳”(例如 ),构造出长度为 的路径。 因此,只要从节点 1 到节点 存在一条长度为 的路径,且 ,并且 和 的奇偶性相同,那么轩轩就可以通过“拖延时间”的方式,让路径长度恰好凑成 。
-
最短路转换:
- 如果 是偶数,我们需要找到从 1 到 的最短偶数长度路径。如果这个最短路径 ,则答案为
Yes。 - 如果 是奇数,我们需要找到从 1 到 的最短奇数长度路径。如果这个最短路径 ,则答案为
Yes。
- 如果 是偶数,我们需要找到从 1 到 的最短偶数长度路径。如果这个最短路径 ,则答案为
-
拆点 BFS: 我们可以使用广度优先搜索(BFS)来求最短路。为了区分奇偶,我们将每个点的状态拆分为两个:
dist[u][0]:到达节点 的最短偶数步数。dist[u][1]:到达节点 的最短奇数步数。 BFS 过程中,从 走到相邻节点 时,步数的奇偶性会翻转(偶 奇,奇 偶)。
-
初始化细节: 为了正确处理 的限制(特别是防止孤立点 1 对于 输出 Yes),我们不手动设置
dist[1][0]=0,而是将 1 号点的所有邻居 初始化为dist[v][1]=1并入队。这样可以保证所有计算出的路径长度都至少为 1。
标准 C++ 代码
#include <iostream> #include <vector> #include <queue> #include <cstring> // 用于 memset using namespace std; const int MAXN = 100005; // 邻接表存图 vector<int> adj[MAXN]; // dist[i][0] 表示从 1 到 i 的最短偶数路径长度 // dist[i][1] 表示从 1 到 i 的最短奇数路径长度 int dist[MAXN][2]; int main() { // 1. 优化输入输出效率 ios::sync_with_stdio(false); cin.tie(0); int n, m, q; if (!(cin >> n >> m >> q)) return 0; // 2. 建图 for (int i = 0; i < m; ++i) { int u, v; cin >> u >> v; adj[u].push_back(v); adj[v].push_back(u); } // 3. BFS 初始化 // 将所有距离初始化为 -1,表示不可达 memset(dist, -1, sizeof(dist)); queue<pair<int, int>> que; // 关键点:题目要求 L >= 1,所以路径长度至少为 1。 // 我们从 1 号点的所有邻居开始 BFS,距离记为 1 (奇数)。 // 这样如果 1 号点是孤立的,队列为空,所有 dist 保持 -1,询问都会输出 No。 for (int v : adj[1]) { dist[v][1] = 1; que.push({v, 1}); // {节点, 奇偶性 1} } // 4. BFS 主过程 while (!que.empty()) { int u = que.front().first; int type = que.front().second; // 当前路径长度的奇偶性 (0或1) que.pop(); int next_type = 1 - type; // 走一步后,奇偶性翻转 for (int v : adj[u]) { // 如果到达 v 的这种奇偶性的最短路还没被更新过 if (dist[v][next_type] == -1) { dist[v][next_type] = dist[u][type] + 1; que.push({v, next_type}); } } } // 5. 处理询问 for (int i = 0; i < q; ++i) { int a, l; cin >> a >> l; int type = l % 2; // 询问的 L 是奇数还是偶数 // 判断条件: // 1. 必须存在同奇偶性的路径 (dist != -1) // 2. 最短的同奇偶性路径必须 <= L (因为可以反复横跳凑出 L) if (dist[a][type] != -1 && dist[a][type] <= l) { cout << "Yes" << "\n"; } else { cout << "No" << "\n"; } } return 0; } -
-
0
你好!我是你的 OI 教练。
这道题(CSP-J 2019 T4 加工零件)是一道非常经典的图论(最短路/BFS)题目。它的核心考点不在于复杂的算法模板,而在于思维转换和奇偶性分析。
下面我为你梳理一下解题思路:
1. 题目翻译:从“加工零件”到“图上路径”
题目描述有点绕,我们先把它翻译成人话:
- 工人 想要一个 阶段的零件。
- 这需要他的所有相邻工人提供 阶段的零件。
- 这又需要相邻工人的相邻工人提供 阶段的零件……
- ……
- 直到需要某个人提供 阶段的零件(也就是原材料)。
问题核心:是否存在一条从工人 (轩轩)到工人 的路径,长度恰好为 ? (因为图是无向的,从 找 和从 找 是一样的,我们习惯从起点 开始思考)。
2. 核心性质:反复横跳(奇偶性)
在图中,如果我能用 步从 走到 ,那我就一定能用 步走过去。 为什么? 因为我可以走到 的某个邻居 再立刻走回 (即 ),白白消耗 2 步,回到了原点。
这意味着:
- 如果存在一条从 到 的长度为 的路径。
- 那么一定存在长度为 的路径。
- 结论:只要找到从 到 的最短的路径长度 ,且 与 的奇偶性相同,并且 ,那么 步就一定能到达。
3. 算法设计:拆点 BFS
我们需要求出从 号点到其他所有点的:
- 最短的偶数长度路径(记为
dist[i][0]) - 最短的奇数长度路径(记为
dist[i][1])
这可以通过 BFS(广度优先搜索) 来实现。 普通的 BFS 队列里存的是
(节点 u),现在的队列里存(节点 u, 当前步数的奇偶性 type)。BFS 转移规则:
- 如果你当前在节点 ,步数是偶数(
dist[u][0]),那么走到邻居 后,步数就变成了奇数。我们尝试用dist[u][0] + 1去更新dist[v][1]。 - 如果你当前在节点 ,步数是奇数(
dist[u][1]),那么走到邻居 后,步数就变成了偶数。我们尝试用dist[u][1] + 1去更新dist[v][0]。
4. 处理询问
对于每个询问
a L:- 如果 是偶数:检查
dist[a][0]。如果dist[a][0] <= L,输出Yes,否则No。 - 如果 是奇数:检查
dist[a][1]。如果dist[a][1] <= L,输出Yes,否则No。
(注意:若图不连通或者不存在某种奇偶性的路径,距离应设为无穷大)
5. 代码框架提示
#include <iostream> #include <vector> #include <queue> #include <cstring> // memset using namespace std; const int MAXN = 100005; vector<int> adj[MAXN]; int dist[MAXN][2]; // dist[i][0] 存偶数最短路,dist[i][1] 存奇数最短路 void bfs() { // 初始化 dist 为 -1 或一个很大的数 memset(dist, -1, sizeof(dist)); queue<pair<int, int>> q; // 起点是 1 号点,距离为 0 (偶数) dist[1][0] = 0; q.push({1, 0}); // {节点, 奇偶性} while (!q.empty()) { int u = q.front().first; int type = q.front().second; // 0 或 1 q.pop(); int next_type = 1 - type; // 偶变奇,奇变偶 for (int v : adj[u]) { // 如果从当前状态走一步能让 v 的对应奇偶路径变短(这里是第一次访问即最短) if (dist[v][next_type] == -1) { dist[v][next_type] = dist[u][type] + 1; q.push({v, next_type}); } } } } int main() { // 1. 读入 n, m, q // 2. 读入 m 条边,建立邻接表 adj // 3. 运行 bfs() 预处理所有距离 // 4. 处理 q 个询问,根据 L 的奇偶性判断 Yes/No return 0; }按照这个思路,这道题就变成了纯粹的 BFS 模板题。加油!
-
0
C++ :
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<cmath> #include<queue> 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<int> 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]+z<ji[y]){//因为边权都是1,所以偶数肯定是由奇数+1得来,奇数也同理 ji[y]=ou[x]+z; if(!v[y]) q.push(y),v[y]=1; } if(ji[x]+z<ou[y]){ ou[y]=ji[x]+z; if(!v[y]) q.push(y),v[y]=1; } } } }//正常的SPFA int main() { scanf("%d%d%d",&t,&n,&m); for(int i=1,u,r;i<=n;i++){ scanf("%d%d",&u,&r); add(u,r); add(r,u); } SPFA(); int a,l; while(m--){ scanf("%d%d",&a,&l); if(l%2==0){ if(ou[a]>l){ 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; }
- 1
信息
- ID
- 3818
- 时间
- 1000ms
- 内存
- 128MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者