拓扑排序(差分约束) - NOIP 2013 车站分级 - 洛谷 P1983

拓扑排序 - NOIP 2013 车站分级 - 洛谷 P1983

一条单向的铁路线上,依次有编号为1, 2, …, n 的n个火车站。

每个火车站都有一个级别,最低为1级。

现有若干趟车次在这条线路上行驶,每一趟都满足如下要求:如果这趟车次停靠了火车站x,则始发站、终点站之间所有级别大于等于火车站x的都必须停靠。(注意:起始站和终点站自然也算作事先已知需要停靠的站点)

例如,下表是5趟车次的运行情况。

其中,前4趟车次均满足要求,而第5趟车次由于停靠了3号火车站(2级)却未停靠途经的6号火车站(亦为2级)而不满足要求。
在这里插入图片描述
现有m趟车次的运行情况(全部满足要求),试推算这n个火车站至少分为几个不同的级别。

输入格式

第一行包含 2 个正整数 n, m,用一个空格隔开。

第 i + 1 行(1 ≤ i ≤ m)中,首先是一个正整数 si(2 ≤ si ≤ n),表示第 i 趟车次有 si 个停靠站;接下来有si个正整数,表示所有停靠站的编号,从小到大排列。

每两个数之间用一个空格隔开。输入保证所有的车次都满足要求。

输出格式

输出只有一行,包含一个正整数,即 n 个火车站最少划分的级别数。

数据范围

1 ≤ n , m ≤ 1000 1≤n,m≤1000 1n,m1000

输入样例:

9 3 
4 1 3 5 6 
3 3 5 6 
3 1 5 9 

输出样例:

3

分析:

每 个 车 站 最 少 1 级 , 输 入 一 系 列 约 束 条 件 能 够 得 到 一 些 车 站 之 间 的 大 小 约 束 关 系 。 每个车站最少1级,输入一系列约束条件能够得到一些车站之间的大小约束关系。 1

我 们 可 以 在 未 停 靠 的 点 A 与 停 靠 的 点 B 之 间 连 一 条 边 , 得 到 B > A 即 B ≥ A + 1 。 我们可以在未停靠的点A与停靠的点B之间连一条边,得到B>A即B≥A+1。 ABB>ABA+1

问 题 转 化 为 差 分 约 束 的 问 题 , 我 们 先 跑 一 遍 拓 扑 排 序 , 然 后 在 拓 扑 图 上 求 最 长 路 即 可 。 问题转化为差分约束的问题,我们先跑一遍拓扑排序,然后在拓扑图上求最长路即可。

难点在于:

对 于 每 条 路 线 ( 约 束 条 件 ) , 我 们 都 要 在 未 停 靠 点 和 停 靠 点 的 任 意 两 点 之 间 建 立 一 条 边 , 是 n 2 级 别 的 边 。 对于每条路线(约束条件),我们都要在未停靠点和停靠点的任意两点之间建立一条边,是n^2级别的边。 线()n2

极 端 情 况 下 , 我 们 可 能 有 1000 条 路 线 , 每 条 路 线 最 多 建 100 0 2 条 边 , 最 终 达 到 1 0 9 级 别 的 边 , 会 超 时 + 超 内 存 。 极端情况下,我们可能有1000条路线,每条路线最多建1000^2条边,最终达到10^9级别的边,会超时+超内存。 1000线线10002109+

解决方案:

对 于 这 种 , 两 个 点 集 A 和 B 之 间 , 任 意 两 点 都 要 建 立 一 条 边 的 情 况 , 对于这种,两个点集A和B之间,任意两点都要建立一条边的情况, AB

可 在 这 两 个 点 集 之 间 建 立 一 个 虚 拟 源 点 v e r , 将 两 个 点 集 都 向 虚 拟 源 点 v e r 建 边 , 可在这两个点集之间建立一个虚拟源点ver,将两个点集都向虚拟源点ver建边, verver

可 以 将 ∣ A ∣ × ∣ B ∣ 条 边 转 化 为 ∣ A ∣ + ∣ B ∣ 条 边 。 可以将|A|×|B|条边转化为|A|+|B|条边。 A×BA+B

本 题 , 我 们 将 所 有 未 停 靠 点 向 虚 拟 源 点 v e r 连 接 一 条 权 值 为 0 的 边 , v e r 向 所 有 停 靠 点 连 接 一 条 权 值 为 1 的 边 。 本题,我们将所有未停靠点向虚拟源点ver连接一条权值为0的边,ver向所有停靠点连接一条权值为1的边。 ver0ver1

第 i 条 路 线 的 虚 拟 源 点 v e r = n + i , 通 过 增 加 偏 移 量 n , 建 立 m 个 点 。 第i条路线的虚拟源点ver=n+i,通过增加偏移量n,建立m个点。 i线ver=n+inm

然 后 再 n + m 个 点 的 图 内 跑 拓 扑 排 序 , 再 跑 最 长 路 。 然后再n+m个点的图内跑拓扑排序,再跑最长路。 n+m

输 出 m a x 1 ≤ i ≤ n d i s [ i ] 即 可 。 输出max_{1≤i≤n}dis[i]即可。 max1indis[i]

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

const int N=2010, M=1e6+10;

int n,m;
int e[M],ne[M],w[M],h[N],idx;
int d[N];
int dis[N];
int q[N],hh,tt=-1;
bool is_stop[N];

void add(int a,int b,int c)
{
    e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;
    d[b]++;
}

void topsort()
{
    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];
            d[j]--;
            if(!d[j]) q[++tt]=j;
        }
    }
}

int cal()
{
    for(int i=1;i<=n;i++) dis[i]=1;
    for(int i=0;i<n+m;i++)
    {
        int u=q[i];
        for(int j=h[u];~j;j=ne[j])
        {
            int k=e[j];
            dis[k]=max(dis[k],dis[u]+w[j]);
        }
    }
    
    int res=0;
    for(int i=1;i<=n;i++) res=max(res,dis[i]);
    
    return res;
}

int main()
{
    scanf("%d%d",&n,&m);
    
    memset(h,-1,sizeof h);
    for(int i=1;i<=m;i++)
    {
        int s,stop,st=n,ed=1;
        memset(is_stop,false,sizeof is_stop);
        scanf("%d",&s);
        while(s--)
        {
            scanf("%d",&stop);
            st=min(st,stop), ed=max(ed,stop);
            is_stop[stop]=true;
        }
        
        int ver=n+i;
        for(int j=st;j<=ed;j++)
            if(is_stop[j]) add(ver,j,1);
            else add(j,ver,0);
    }
    
    topsort();
    
    printf("%d\n",cal());
    
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值