【车站分级】

题目

代码

#include<bits/stdc++.h>
using namespace std;
const int N = 2010, M = 1e6+10; //顶点的数量 = 车站(实际顶点)数量n + 虚拟顶点数量m(等于车次数) 边的数量 = 车站数量n * 车次数量 m
int n, m;
bool st[1010]; //实际点状态
int dist[N]; 
int q[N]; 
int d[N]; 
int e[M], h[N], ne[M], idx, w[M];
void add(int a, int b, int c)
{
    e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
    d[b]++;
}
void toposort()
{
    int hh = 0, tt = -1;
    for(int i = 1; i <= n+m; i++)
    {
        if(!d[i]) q[++tt] = i;
    }
    while(hh <= tt)
    {
        int u = q[hh++];
        
        for(int i = h[u]; ~i; i = ne[i])
        {
            int j = e[i];
            dist[j] = max(dist[j], dist[u] + w[i]);
            if(--d[j] == 0) q[++tt] = j;
        }

    }

}

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    
    cin >> n >> m;
    memset(h, -1, sizeof h);
    for(int i = 1; i <= m; i++)
    {
        memset(st, 0, sizeof st);
        int cnt;
        cin >> cnt;
        int start = n, end = 1;
        for(int j = 1; j <= cnt; j++)
        {
            int stop;
            cin >> stop;
            start = min(start, stop);
            end = max(end, stop);
            st[stop] = true;
        }
        
        int ver = n + i;
        for(int j = start; j <= end; j++)
        {
            if(st[j]) add(ver, j, 1);
            else add(j, ver, 0);
        }
    }
    for (int i = 1; i <= n; i++) dist[i] = 1;
    
    toposort();
    int res = 0;
    for(int i = 1; i <= n; i++) res = max(res, dist[i]);
    
    cout << res;
    return 0;
}

分析


首先让我们提取m个序列提供的信息:

我们把一个序列的元素放在一个集合A里,求它的补集B,则A中的元素一定大于等于一个数,这个数是A中所有元素的级别的最小值,而B中的元素一定小于这个数。也就是说,A中元素一定大于等于B中元素(级别)加1,同时全集上的任意元素又满足大于等于1(这里看似是级别大于等于1,实际上是为了计算数量初始化为1,哪怕级别大于等于2求数量也得这么初始化)。

从存储上来看:我们是可以用邻接表的,这个大小不过就是1000×1000。但是从操作上来看,我们不能够。

每一个序列产生的边最多应该是500×500。就相当于A中所有元素映射到B中所有元素。在这种情况下,操作数字就等于A集合的大小乘以B集合的大小。他们之和是1000,各自为500的时候,就是这个乘积最大的时候,再乘以序列数目的话,这个数量达到了10的8次方。但是如果我们采用如下的连接方式,它就变成了10的6次方,也就是1000个序列,每个序列我们只要连1000次,这个1000次与实际顶点数目是相同的。

(这里n, m不是题目中的,是A,B集合的大小, n + m = 实际顶点数 / 车站数)


但是在这种情况下,我们每一个序列要引入一个虚拟节点,我们有1000个车次,1000个序列,也就是1000个虚拟节点,这就是为什么我们总的节点数目有2000个,而边的数目是 m×n是10的6次方个。


我们假设一个总源头点,从这个点出发连接了1000个实际顶点和1000个虚拟顶点。而1000个实际顶点到原点的距离我们定为1这代表至少要有一个级别。


B集合当中的元素到虚拟节点,这个距离我们定成0。虚拟节点到A元素当中的节点,我们把这个长度定为一,这样就相当于B元素到A元素的距离为1。如果我们进行适当的更新,A元素的到原点的距离应该是B元素到原点的距离大1。当然这是就一个序列的连接关系而看。

正是因为有这种相互制约的关系,我们要从一个入度为零的节点开始,因为我们知道这时候他的距离一定是1。所以这就是为什么我们要通过拓扑排序,按照拓扑排序的顺序来把一个节点作为起点对邻接的所有终点进行更新,一直到最后。然后我们再根据每一个顶点对级别数目的最小要求取一个群体的最大值。这样就可以在满足所有节点对于级别数目的要求的前提下取得最小值。
 

注意

别用memset初始1。

因为idx第一个为0,所以h要用-1初始化,如果就用默认的0,那么idx起始值设为1即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值