3 条题解

  • 0
    @ 2025-11-28 13:53:49

    这是一份标准的 AC 代码。

    解题核心思路

    1. 奇偶性分析:在一个无向图中,如果存在一条从 uuvv 长度为 dd 的路径,那么可以通过在某条边上“反复横跳”(例如 uxyxvu \to \dots \to x \to y \to x \to \dots \to v),构造出长度为 d+2,d+4,d+2, d+4, \dots 的路径。 因此,只要从节点 1 到节点 aa 存在一条长度为 dd 的路径,且 dLd \le L,并且 ddLL奇偶性相同,那么轩轩就可以通过“拖延时间”的方式,让路径长度恰好凑成 LL

    2. 最短路转换

      • 如果 LL 是偶数,我们需要找到从 1 到 aa最短偶数长度路径。如果这个最短路径 L\le L,则答案为 Yes
      • 如果 LL 是奇数,我们需要找到从 1 到 aa最短奇数长度路径。如果这个最短路径 L\le L,则答案为 Yes
    3. 拆点 BFS: 我们可以使用广度优先搜索(BFS)来求最短路。为了区分奇偶,我们将每个点的状态拆分为两个:

      • dist[u][0]:到达节点 uu 的最短偶数步数。
      • dist[u][1]:到达节点 uu 的最短奇数步数。 BFS 过程中,从 uu 走到相邻节点 vv 时,步数的奇偶性会翻转(偶 \to 奇,奇 \to 偶)。
    4. 初始化细节: 为了正确处理 L1L \ge 1 的限制(特别是防止孤立点 1 对于 L=2L=2 输出 Yes),我们手动设置 dist[1][0]=0,而是将 1 号点的所有邻居 vv 初始化为 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
      @ 2025-11-28 13:48:43

      你好!我是你的 OI 教练。

      这道题(CSP-J 2019 T4 加工零件)是一道非常经典的图论(最短路/BFS)题目。它的核心考点不在于复杂的算法模板,而在于思维转换奇偶性分析

      下面我为你梳理一下解题思路:

      1. 题目翻译:从“加工零件”到“图上路径”

      题目描述有点绕,我们先把它翻译成人话:

      • 工人 aa 想要一个 LL 阶段的零件。
      • 这需要他的所有相邻工人提供 L1L-1 阶段的零件。
      • 这又需要相邻工人的相邻工人提供 L2L-2 阶段的零件……
      • ……
      • 直到需要某个人提供 00 阶段的零件(也就是原材料)。

      问题核心:是否存在一条从工人 11(轩轩)到工人 aa 的路径,长度恰好LL? (因为图是无向的,从 aa11 和从 11aa 是一样的,我们习惯从起点 11 开始思考)。

      2. 核心性质:反复横跳(奇偶性)

      在图中,如果我能用 dd 步从 11 走到 uu,那我就一定能用 d+2d+2 步走过去。 为什么? 因为我可以走到 uu 的某个邻居 vv 再立刻走回 uu(即 uvuu \to v \to u),白白消耗 2 步,回到了原点。

      这意味着:

      • 如果存在一条从 11aa 的长度为 dd 的路径。
      • 那么一定存在长度为 d+2,d+4,d+6,d+2, d+4, d+6, \dots 的路径。
      • 结论:只要找到从 11aa最短的路径长度 dd,且 ddLL奇偶性相同,并且 dLd \le L,那么 LL 步就一定能到达。

      3. 算法设计:拆点 BFS

      我们需要求出从 11 号点到其他所有点的:

      1. 最短的偶数长度路径(记为 dist[i][0]
      2. 最短的奇数长度路径(记为 dist[i][1]

      这可以通过 BFS(广度优先搜索) 来实现。 普通的 BFS 队列里存的是 (节点 u),现在的队列里存 (节点 u, 当前步数的奇偶性 type)

      BFS 转移规则

      • 如果你当前在节点 uu,步数是偶数dist[u][0]),那么走到邻居 vv 后,步数就变成了奇数。我们尝试用 dist[u][0] + 1 去更新 dist[v][1]
      • 如果你当前在节点 uu,步数是奇数dist[u][1]),那么走到邻居 vv 后,步数就变成了偶数。我们尝试用 dist[u][1] + 1 去更新 dist[v][0]

      4. 处理询问

      对于每个询问 a L

      • 如果 LL偶数:检查 dist[a][0]。如果 dist[a][0] <= L,输出 Yes,否则 No
      • 如果 LL奇数:检查 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
        @ 2025-9-10 9:11:36

        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
        上传者