3048: 【普及+/提高】【P1983】车站分级

内存限制:128 MB 时间限制:1.000 S
评测方式:文本比较 命题人:
提交:4 解决:4

题目描述

一条单向的铁路线上,依次有编号为 lns="http://www.w3.org/1998/Math/MathML">1,2,,的 lns="http://www.w3.org/1998/Math/MathML">个火车站。每个火车站都有一个级别,最低为 lns="http://www.w3.org/1998/Math/MathML">1 级。现有若干趟车次在这条线路上行驶,每一趟都满足如下要求:如果这趟车次停靠了火车站 lns="http://www.w3.org/1998/Math/MathML">,则始发站、终点站之间所有级别大于等于火车站lns="http://www.w3.org/1998/Math/MathML"> 的都必须停靠。(注意:起始站和终点站自然也算作事先已知需要停靠的站点)

例如,下表是lns="http://www.w3.org/1998/Math/MathML">5趟车次的运行情况。其中,前lns="http://www.w3.org/1998/Math/MathML">4 趟车次均满足要求,而第 lns="http://www.w3.org/1998/Math/MathML">5 趟车次由于停靠了 lns="http://www.w3.org/1998/Math/MathML">3 号火车站(lns="http://www.w3.org/1998/Math/MathML">2 级)却未停靠途经的 lns="http://www.w3.org/1998/Math/MathML">6 号火车站(亦为 lns="http://www.w3.org/1998/Math/MathML">2 级)而不满足要求。

现有 lns="http://www.w3.org/1998/Math/MathML"> 趟车次的运行情况(全部满足要求),试推算这lns="http://www.w3.org/1998/Math/MathML"> 个火车站至少分为几个不同的级别。

输入

第一行包含 lns="http://www.w3.org/1998/Math/MathML">2 个正整数 lns="http://www.w3.org/1998/Math/MathML">,,用一个空格隔开。

第 lns="http://www.w3.org/1998/Math/MathML">+1 行lns="http://www.w3.org/1998/Math/MathML">(1)中,首先是一个正整数 lns="http://www.w3.org/1998/Math/MathML">(2),表示第lns="http://www.w3.org/1998/Math/MathML"> 趟车次有 lns="http://www.w3.org/1998/Math/MathML"> 个停靠站;接下来有lns="http://www.w3.org/1998/Math/MathML">个正整数,表示所有停靠站的编号,从小到大排列。每两个数之间用一个空格隔开。输入保证所有的车次都满足要求。

输出

一个正整数,即 lns="http://www.w3.org/1998/Math/MathML"> 个火车站最少划分的级别数。

样例输入 复制

9 2 
4 1 3 5 6 
3 3 5 6 

样例输出 复制

2

提示

对于lns="http://www.w3.org/1998/Math/MathML">20%的数据,lns="http://www.w3.org/1998/Math/MathML">1,10

对于 lns="http://www.w3.org/1998/Math/MathML">50%的数据,lns="http://www.w3.org/1998/Math/MathML">1,100

对于 lns="http://www.w3.org/1998/Math/MathML">100%的数据,lns="http://www.w3.org/1998/Math/MathML">1,1000

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

做出一张图(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;
}


/*
因为火车都要停靠比它高级(大于等于)的车站,所以其它不停的就是比它级别小(小于)的车站,现在求最高等级的车站是几级。
在所有不停的站的级别小于停靠的站的情况下,我们可以做出一张图(A指向B表示A车站级别大于B车站)
*/
#include<bits/stdc++.h>
using namespace std;
#define MAXN 1010
int n,m,ans=1;
vector <int> p[MAXN];
bool b[MAXN],c[MAXN][MAXN]; //b[i]表示是否是停靠站, c[i][j]表示j>i的级别
int a[MAXN],outd[MAXN],ind[MAXN];
struct node {
	int x,d;
};
queue <node> q;
int main(){
    cin >> n >> m;
    for (int i = 0;i < m; i++) {
    	int s;
    	cin >> s;
    	memset(b,0,sizeof(b));
    	for (int j = 1; j <= s; j++) {
    		cin >> a[j];
    		b[a[j]] = true;
		}
    	for (int j = a[1]; j <= a[ s ]; j++) {
    		if (!b[j]) { //枚举站点,若不是已停靠的就小于所有停靠站的等级
    			for (int k = 1; k <= s; k++) { //枚举已停靠站点
    				if (!c[j][a[k]]) {  //没配置过边的有效,重边只留一条.因为不同的车次,存在两条一样的边是很正常的,但没有必要保留多个。
    					c[j][a[k]] = true;
    					outd[a[k]]++;
    					ind[j]++;
    					p[a[k]].push_back(j);
					}
				}
			}
		}
	}
	for (int i = 1;i <= n; i++) {
		if (ind[i] == 0) {
			q.push((node){i,1});
		}
	}
	while (!q.empty()) {
		node t=q.front();
		q.pop();
		if (outd[t.x] == 0 ) {
			ans = max(ans, t.d);
		}
		for (int i = 0, sz = p[t.x].size(); i < sz ; i++) {
			ind[p[t.x][i]]--;
			if (ind[p[t.x][i]] == 0 ) {
				q.push((node){p[t.x][i], t.d+1});
			}
		}
	}
	cout << ans <<endl;
	return 0;
}