【第二十四题】逻辑闭环(北理工/北京理工大学/程序设计方法与实践/小学期 )

目录

Description

思路

代码 


Description

小张是一位推理迷,他非常喜欢看侦探小说与侦探电影。同时他也会玩一些推理游戏,在侦探游戏中,小张需要发掘事件之间的联系。通过一条线索,他能够通过事件A推理出事件B。如果通过某一个事件A能够推出事件A本身,那么他就能够完成推理。现在按照顺序给出 m 条线索,请你算出他最少能够用前几条线索能够形成逻辑闭环完成推理。

Input 第一行 n,m(1 \leq n,m \leq 300000 ) 两个数,表示事件数和线索数。 接下来 m 行,每行两个数 A,B(1 \leq A,B \leq n ) ,表示事件A能够推出事件B。  

Output 一行一个数,表示最少能够用前几条线索形成逻辑闭环完成推理。若无法完成输出-1。

思路

仍然是bfs。

其实这类题说简单就一个bfs,但是细节会有很多变化。

比如之前的22出现非最短步数情况。

这道24又多了很多细节:

  1. 逻辑闭环不需要记录首尾,只要前几个中有可以构成闭环的数字即可
  2. 既然是闭环,那么任何数都可以当做开头,所以你从A搜到B可以,也可以从B搜到A
  3. 我们怎么确定哪一个在闭环里?难道是建立一个数组储存前几个读入的,然后每读入一个数,就对整个数组中每一个数都bfs一次?太累了,绝对TLE。不妨优化一下:假设前n-1个数不能形成闭环,而有了第n对数就形成闭环,说明第n对数必然在闭环内,这样每次读入一个新的数就bfs一次即可。
  4. 照着这个思想,我第一次TLE掉4个hhh,如果算法没有什么问题,或者说同样的算法别人能过你就不能过 ,那必然是你多了很多冗杂的步骤,之后就开始分析。
  5. 此题不要求最短步数,所以其实只要能形成逻辑闭环即可,不需要用vis数组计步,用vis数组的话需要判断,memset,都是浪费时间。
  6. 具体思路框架在代码里找吧

代码 

TLE掉4个的版本

#include<cstdio> 
#include<cstring> 
#include<map> 
#include<vector> 
#include<queue> 
#define SIZE 300010 
//int step[SIZE]; 
int cause[SIZE] = { 0 }; 
int result[SIZE] = { 0 }; 
int step; 
std::map<int, std::vector<int>> graph; 
std::queue<int> q; 
bool bfs(int h2, int h1,bool vis[]); 
int main(void) 
{ 
// freopen("input.txt", "r", stdin); 
  int n, m, temp1, temp2; 
    scanf("%d %d", &n, &m); 
    for (step = 1; step <= m; step++) 
   { 
      bool vis[SIZE] = { false }; 
        scanf("%d %d", &temp1, &temp2); 
        graph[temp1].push_back(temp2); 
     if (bfs(temp2, temp1, vis))//满足之前出现过的条件才bfs 
        { 
          printf("%d\n", step); 
          return 0; 
      } 
  } 
  //走到这里就是没有结果了 
  printf("-1\n"); 
    return 0; 
} 
bool bfs(int h2,int h1,bool vis[])//从结尾bfs到第一个的开头 
{ 
    int top = h2; 
  q.push(h2); 
    while (!q.empty()) 
 { 
      //取出 
       top = q.front(); 
       q.pop(); 
       //放入邻居 
     for (auto x : graph[top]) 
      { 
          if (x == h1) //找到逻辑闭环 
              return true; 
           if (!vis[x]) 
           { 
              q.push(x); 
             vis[x] = true; 
         } 
      } 
  } 
  //最后空了,就是bfs失败 
 return false;                            
}

去掉冗杂步骤ac版本

#include<cstdio> 
#include<cstring> 
#include<map> 
#include<vector> 
#include<queue> 
#define SIZE 300010 
int step;
std::map<int, std::vector<int>> graph;
std::queue<int> q;
bool bfs(int h2, int h1);
int main(void)
{
    //freopen("input.txt", "r", stdin); 
    int n, m, temp1, temp2;
    scanf("%d %d", &n, &m);
    for (step = 1; step <= m; step++)
    {
        scanf("%d %d", &temp1, &temp2);
        graph[temp1].push_back(temp2);
        if (bfs(temp2, temp1))//满足之前出现过的条件才bfs 
        {
            printf("%d\n", step);
            return 0;
        }
    }
    //走到这里就是没有结果了 
    printf("-1\n");
    return 0;
}
bool bfs(int h2, int h1)//从结尾bfs到第一个的开头 
{
    int top = h2;
    q.push(h2);
    while (!q.empty())
    {
        //取出 
        top = q.front();
        q.pop();
        //放入邻居 
        for (auto x : graph[top])
        {
            if (x == h1) //找到逻辑闭环 
                return true;
            q.push(x);
        }
    }
    //最后空了,就是bfs失败 
    return false;
}

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

亦梦亦醒乐逍遥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值