2 条题解

  • 1
    @ 2025-10-26 22:17:43

    模拟几种可能的路线组合后,我们可以发现所有路线可以分成两种类型:

    1.这个路线会经过其他路线都不能经过的城市,也就是必须选择的路线。

    2.这个路线会经过的所有城市也可以在其他路线中被经过,所以这个路线是可以被其他路线替代的。

    那么这m个路线我们减去第一种必须选择的路线,剩下的路线可以相互代替,组合。并且两条路线就能构成三种不同的路线组合,分别为[1],[2],[1,2].

    那我们的核心策略就是找到必须选择的路线并减去,判断剩下的路线是否大于2就可以了。

    如果有个城市不被任何路线经过,那么直接输出No并结束程序即可

    #include<bits/stdc++.h>
    using namespace std;
    #define LL long long
    #define pii pair<int,int>
    #define pu push_back
    
    LL mod=1;
    void babason(){
        int n,m;cin>>n>>m;
        vector<int> a(m+1);//代表每条路线经过的城市数量
        for(int i=1;i<=m;i++){
            cin>>a[i];
        }
        vector<vector<int>> b(m+1,vector<int>(n+1));//表示第i条路线会经过的第j个城市
        vector<int> num_line(n+1,0);//表示第i个城市,会被多少条路线经过
        
        for(int i=1;i<=m;i++){//一边输入路线会经过的城市,一边累加这个城市被不同路线经过的次数
            for(int j=1;j<=a[i];j++){
                cin>>b[i][j];
                num_line[b[i][j]]++;    
            }
        }
    
        for(int i=1;i<=n;i++){//如果有一个城市不能被任意条路线经过,那么就输出NO并结束程序
            if(num_line[i]==0){
                cout<<"NO";return;
            }
        }
        
        int num_n=m;//表示一共有m条路线
        
        for(int i=1;i<=m;i++){
            for(int j=1;j<=a[i];j++){
                if(num_line[b[i][j]]==1){//如果这条路线经过的城市num_line为1,那么这条路线一定属于第一种,需要从num_n中减去
                    num_n--;
                    break;
                }
            }
        }
        
        if(num_n>=2){//判断并输出
            cout<<"YES\n";
        }
        else {
            cout<<"NO";
        }
    }
    
    int main(){
        cin.tie(0);
        cout.tie(0);
        ios::sync_with_stdio(0);
        //int t;cin>>t;while(t--)
        babason();
        return 0;
    }
    
    • 0
      @ 2025-10-30 18:38:43

      Python解法,参考DFS解法

      主要利用:

      1. 位运算状态压缩 - 用二进制位表示城市覆盖状态
      2. 二分DFS回溯搜索 - 枚举所有路线组合 同时利用每个决策
      3. 多重剪枝优化 - 大幅减少搜索空间
      4. 排序启发式 - 优先搜索高价值路线

      通过将城市集合转换为二进制掩码存储, 利用位或运算 合并路线覆盖范围。

      def main():
          import sys
          #import sys 是常量时间操作,不影响算法复杂度
          sys.setrecursionlimit(1000000)
          #递归限制一百万次(其实没那么多,纯模版 不妨碍运行速度) 
        #但是程序该崩还得崩 栈空间有限
        #它的作用是让程序直接崩溃而不是优雅报错
          data = sys.stdin.read().split()
          it = iter(data)
          #创建迭代器 但是这里注意 不能写成it = list(map(int, iter(data))) 否则立即求值 内存o(n)
          n, m = int(next(it)), int(next(it))
          a = [int(next(it)) for _ in range(m)]
          #路线所能到达的城市数量
          
      
      
          routes = []
          #创建存储列表 用于存储每条路利用二进制转换后表示的存储的城市数量
          for i in range(m):
              cities = [int(next(it)) for _ in range(a[i])]
              #记录单独一条路所能到达的城市
              mask = 0
              #存储与值 (可以表示到达的城市数量)
              for city in cities:
                  mask |= (1 << (city - 1))
                  #city - 1 因为0-bassed
              routes.append(mask)
          ###一条路所代表的覆盖的城市
          #同样 把小叶子想去的城市个数也转换成二进制表示 这里的-1 是十分重要的 让所有低位都变成1
          full = (1 << n) - 1
          #定义变量
          solutions = 0
          
          """前面其实主要全部做的是输入数据的处理 就是把数据全部转换为二进制 方便进行
          位运算 主要是利用了计算机的计算原理 并且利用二进制可以表示的类型极多 同时计算机内部
          对二进制运算极快的性质 因此用二进制进行存储状态 在进行运算 表示状态 
          下面才正式进行主程序的判断 主要利用了dfs 以及剪枝1和剪枝2(减少运算时间)"""
      
          """声明要修改外层函数的solutions 如果不明 
          实际上是创建了一个新的局部变量,外局的solutions是不会改变的 
              
          为什么solution要定义在外层?
          这其实涉及到dfs的核心思想 如果定义在外层 那么所有dfs共用一个计时器 
          无论递归多深 都修改同一个solutions 但是如果定义在dfs内部 每个递归分支都将会
          有他自己的solutions副本 修改不会影响到其他分支 无法正常计数
              
          为什么需要共享solutions? 
          因为这里是一个剪枝 任何分支只要找到三个解 所有分支都要停止
          如果不用nonlocal 无法提前终止 必须搜索完整棵树  
          (剪枝让dfs比暴力枚举高效的多)"""
      
      #i 是考虑到第n条路线 mask是当前已经覆盖的城市集合
          def dfs(i, mask):
              #i是dfs递归函数的参数 在递归调用的过程中自动更新和传递
              nonlocal solutions
              if solutions >= 3:
                  return
              if mask == full:
                  solutions += 1
                  return
              if i == m:
                  return
              
              # 剪枝1:检查剩余路线是否能完成覆盖 就是检查最后还有没有正确答案
              #没有的话直接结束了
              #mask 代表 目前已经选择的所有路线的总覆盖情况
              remaining = mask
              for j in range(i, m):
                  remaining |= routes[j]
              if remaining != full:
                  return
              #二分dfs
              dfs(i + 1, mask | routes[i])
            #剪枝2:下面的if起重要的剪枝作用 如果上面的分支已经满足条件 就不进行下面这个分支
            #可以优化50%以上的搜索量 起一个提前终止的作用
              if solutions >= 3:
                  return
              dfs(i + 1, mask)
          
          #先对每条路径能到达的城市进行排序 使能到达城市多的路径先在前面
          #优化dfs的搜索次数 大幅度减少运行时间
      
          #一个对于1的数量进行由大到小的自定义排序 
          routes.sort(key=lambda x: bin(x).count('1'), reverse=True)
          #主程序 调用dfs函数 i和mask的初始值都是0
          dfs(0, 0)
      
          print("YES" if solutions >= 3 else "NO")
      
      if __name__ == "__main__":
          main()
         """对于不同的路径选择 可能会产生相同的mask 比如对于路径[1][2][1,2](这里的1和2代表的可以去的城市)
       选择[1][2],[1][1,2],[2][1,2] 这三种情况与只选择[1,2]路径得到的路线 最后到达的城市都是12 所以在转换二进制的时候 
       路线虽然不同但是得到的mask都相同 所以本题在处理的时候选择的是判断solution 只要这条路线能到全部城市 就进行计数 
       对于相同的mask 不应该进行舍弃 否则会出现最后结果一样的路线但是路线内所含路径不同的路线被舍弃(应该被计数)"""
          
      
      
      #无注释版本:
      def main():
          import sys
          sys.setrecursionlimit(1000000)
          
          data = sys.stdin.read().split()
          it = iter(data)
          n, m = int(next(it)), int(next(it))
          a = [int(next(it)) for _ in range(m)]
          
          routes = []
          for i in range(m):
              cities = [int(next(it)) for _ in range(a[i])]
              mask = 0
              for city in cities:
                  mask |= (1 << (city - 1))
              routes.append(mask)
          
          full = (1 << n) - 1
          solutions = 0
          
          def dfs(i, mask):
              nonlocal solutions
              if solutions >= 3:
                  return
              if mask == full:
                  solutions += 1
                  return
              if i == m:
                  return
              
              remaining = mask
              for j in range(i, m):
                  remaining |= routes[j]
              if remaining != full:
                  return
              
              dfs(i + 1, mask | routes[i])
              if solutions >= 3:
                  return
              dfs(i + 1, mask)
          
          routes.sort(key=lambda x: bin(x).count('1'), reverse=True)
          dfs(0, 0)
          
          print("YES" if solutions >= 3 else "NO")
      
      if __name__ == "__main__":
          main()
      
      • 1

      信息

      ID
      1111
      时间
      1000ms
      内存
      256MiB
      难度
      10
      标签
      递交数
      43
      已通过
      1
      上传者