zoukankan      html  css  js  c++  java
  • P1983 [NOIP2013 普及组] 车站分级

    题目传送门

    一、解题思路

    ​这道题我们可以理解为凡是停靠过的站,那么这个站就比没停靠过的站级别高。

    做出一张图(A指向B表示A车站级别大于B车站)[用的是样例1]

    其实,本题的本质是在强调“层次”,有严格的层次感,就可以想到用拓扑排序,去点、去线,一路向前,最终的步数就是几层。不太好想的,就是如何建图,拓扑排序嘛,要想使用,必须有严格的大小关系,才好创建边。本题的大小关系是由“不停靠站点”的等级小于“停靠站点”这个推理得到的。

    是由小到大建边,还是由大到小建边都是一回事,下面的代码把两种方式都实现了一次。

    二、等级高向等级低连边

    #include <bits/stdc++.h>
    
    using namespace std;
    /**
      思路:停靠站->非停靠站连边,然后用拓扑排序求深度。
     */
    const int N = 1010;  //题目中要求结点数是1000个上限
    int n, m;            //n个车站,m个车次
    vector<int> edge[N]; //邻接表
    int in[N];           //入度数组
    bool flag[N];        //flag[i]标识i是不是停靠站点
    bool st[N][N];       //用来判断是不是已经存在了i到j的边
    int ans;             //答案
    int a[N];            //停靠站信息
    
    //结构体,携带两个信息,一个是哪个车站,另一个是几级
    struct Node {
        int num;
        int step;
    };
    
    //拓扑排序,计算出层次
    void topSort() {
        //拓扑排序
        queue<Node> q;      //拓扑用队列
        //查找所有入度为0的结点入队列,第一个参数是结点号,第二个参数是步数
        for (int i = 1; i <= n; ++i) if (!in[i]) q.push({i, 1});
        //开始拓扑套路
        while (!q.empty()) {
            //结点ID
            int u = q.front().num;
            //步数
            int step = q.front().step;
            q.pop();
            //注意修改ans的值,保持最大值
            ans = max(ans, step);
            for (auto v:edge[u]) {
                in[v]--;
                if (!in[v]) q.push({v, step + 1});
            }
        }
    }
    
    int main() {
        cin >> n >> m;
        while (m--) {
            //每次重新初始化状态数组
            memset(flag, 0, sizeof(flag));
            int s;          //本轮的停靠站数量
            cin >> s;
            //读入停靠站信息
            for (int i = 1; i <= s; ++i) cin >> a[i], flag[a[i]] = true; //标识是停靠站
    
            //遍历出发站到终点站,这里可不是全部车站啊!只有在范围内的才能明确等级关系啊!!!注意
            //这里a[1]是指起点,a[s]是指终点,就是这个车次的出发点到结束点,这中间有停靠的,有不停靠的,
            // 不停靠的站等级一定是小于停靠的
            for (int i = a[1]; i <= a[s]; ++i)
                //如果不是停靠站,那就是等级低的站,需要连边~,停靠站->非停靠站连边
                if (!flag[i]) {
                    int target = i;
                    for (int j = 1; j <= s; ++j) {
                        int source = a[j];
                        if (!st[source][target]) {//没配置过边的有效,重边只留一条.因为不同的车次,
                            // 存在两条一样的边是很正常的,但没有必要保留多个。
                            edge[source].push_back(target);
                            st[source][target] = true;//标识已配置
                            in[target]++;//入度++
                        }
                    }
    
                }
        }
        //拓扑排序
        topSort();
        //输出结果
        cout << ans << endl;
        return 0;
    }
    

    三、等级低向等级高连边

    #include <bits/stdc++.h>
    
    using namespace std;
    /**
     思路:非停靠-->停靠站连边,然后用拓扑排序求深度。
     */
    const int N = 1010;  //题目中要求结点数是1000个上限
    int n, m;            //n个车站,m个车次
    vector<int> edge[N]; //邻接表
    int in[N];           //入度数组
    bool flag[N];         //flag[i]标识是不是停靠站点
    bool st[N][N];       //用来判断是不是已经存在了i到j的边
    int ans;             //答案
    int a[N];            //停靠站信息
    
    //结构体,携带两个信息,一个是哪个车站,另一个是几级
    struct Node {
        int num;
        int step;
    };
    
    //拓扑排序,计算出层次
    void topSort() {
        //拓扑排序
        queue<Node> q;      //拓扑用队列
        //查找所有入度为0的结点入队列,第一个参数是结点号,第二个参数是步数
        for (int i = 1; i <= n; ++i) if (!in[i]) q.push({i, 1});
        //开始拓扑套路
        while (!q.empty()) {
            //结点ID
            int u = q.front().num;
            //步数
            int step = q.front().step;
            q.pop();
            //注意修改ans的值,保持最大值
            ans = max(ans, step);
            for (auto v:edge[u]) {
                in[v]--;
                if (!in[v]) q.push({v, step + 1});
            }
        }
    }
    
    int main() {
        cin >> n >> m;
        while (m--) {
            //每次重新初始化状态数组
            memset(flag, 0, sizeof(flag));
            int s;          //本轮的停靠站数量
            cin >> s;
            //读入停靠站信息
            for (int i = 1; i <= s; ++i) cin >> a[i], flag[a[i]] = true; //标识是停靠站
    
            //遍历出发站到终点站,这里可不是全部车站啊!只有在范围内的才能明确等级关系啊!!!注意
            //这里a[1]是指起点,a[s]是指终点,就是这个车次的出发点到结束点,这中间有停靠的,有不停靠的,
            // 不停靠的站等级一定是小于停靠的
            for (int i = a[1]; i <= a[s]; ++i)
                //如果不是停靠站,那就是等级低的站,需要连边~,非停靠站-->停靠站 连边
                if (!flag[i]) {
                    int source = i; //出发结点,在这里是指非停靠站点
                    for (int j = 1; j <= s; ++j) {
                        int target = a[j];  //a[j]代表所有依靠站
                        if (!st[source][target]) {//没配置过边的有效,重边只留一条.因为不同的车次,
                            // 存在两条一样的边是很正常的,但没有必要保留多个。
                            edge[source].push_back(target);
                            st[source][target] = true;//标识已配置
                            in[target]++;//入度++
                        }
                    }
                }
        }
        //拓扑排序
        topSort();
        //输出结果
        cout << ans << endl;
        return 0;
    }
    
  • 相关阅读:
    用SPSBackup将SharePoint Portal Server 2003站点迁移至新服务器
    对于Outlook 2003垃圾邮件规则的一点意见
    “blog.donews.com 瘫掉?”引发的评论
    jobmet弹出广告事件
    五一节安排
    IE 浏览器的创新
    newland.js的Ioc实现
    关于 CouchDB 的一些知识
    require("http").request(options, cb)的一些研究
    NOSQL数据库大比拼:Cassandra vs MongoDB vs CouchDB vs Redis vs Riak vs HBase
  • 原文地址:https://www.cnblogs.com/littlehb/p/15151946.html
Copyright © 2011-2022 走看看