拓扑排序 - 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 1≤n,m≤1000
输入样例:
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。 我们可以在未停靠的点A与停靠的点B之间连一条边,得到B>A即B≥A+1。
问 题 转 化 为 差 分 约 束 的 问 题 , 我 们 先 跑 一 遍 拓 扑 排 序 , 然 后 在 拓 扑 图 上 求 最 长 路 即 可 。 问题转化为差分约束的问题,我们先跑一遍拓扑排序,然后在拓扑图上求最长路即可。 问题转化为差分约束的问题,我们先跑一遍拓扑排序,然后在拓扑图上求最长路即可。
难点在于:
对 于 每 条 路 线 ( 约 束 条 件 ) , 我 们 都 要 在 未 停 靠 点 和 停 靠 点 的 任 意 两 点 之 间 建 立 一 条 边 , 是 n 2 级 别 的 边 。 对于每条路线(约束条件),我们都要在未停靠点和停靠点的任意两点之间建立一条边,是n^2级别的边。 对于每条路线(约束条件),我们都要在未停靠点和停靠点的任意两点之间建立一条边,是n2级别的边。
极 端 情 况 下 , 我 们 可 能 有 1000 条 路 线 , 每 条 路 线 最 多 建 100 0 2 条 边 , 最 终 达 到 1 0 9 级 别 的 边 , 会 超 时 + 超 内 存 。 极端情况下,我们可能有1000条路线,每条路线最多建1000^2条边,最终达到10^9级别的边,会超时+超内存。 极端情况下,我们可能有1000条路线,每条路线最多建10002条边,最终达到109级别的边,会超时+超内存。
解决方案:
对 于 这 种 , 两 个 点 集 A 和 B 之 间 , 任 意 两 点 都 要 建 立 一 条 边 的 情 况 , 对于这种,两个点集A和B之间,任意两点都要建立一条边的情况, 对于这种,两个点集A和B之间,任意两点都要建立一条边的情况,
可 在 这 两 个 点 集 之 间 建 立 一 个 虚 拟 源 点 v e r , 将 两 个 点 集 都 向 虚 拟 源 点 v e r 建 边 , 可在这两个点集之间建立一个虚拟源点ver,将两个点集都向虚拟源点ver建边, 可在这两个点集之间建立一个虚拟源点ver,将两个点集都向虚拟源点ver建边,
可 以 将 ∣ A ∣ × ∣ B ∣ 条 边 转 化 为 ∣ A ∣ + ∣ B ∣ 条 边 。 可以将|A|×|B|条边转化为|A|+|B|条边。 可以将∣A∣×∣B∣条边转化为∣A∣+∣B∣条边。
本 题 , 我 们 将 所 有 未 停 靠 点 向 虚 拟 源 点 v e r 连 接 一 条 权 值 为 0 的 边 , v e r 向 所 有 停 靠 点 连 接 一 条 权 值 为 1 的 边 。 本题,我们将所有未停靠点向虚拟源点ver连接一条权值为0的边,ver向所有停靠点连接一条权值为1的边。 本题,我们将所有未停靠点向虚拟源点ver连接一条权值为0的边,ver向所有停靠点连接一条权值为1的边。
第 i 条 路 线 的 虚 拟 源 点 v e r = n + i , 通 过 增 加 偏 移 量 n , 建 立 m 个 点 。 第i条路线的虚拟源点ver=n+i,通过增加偏移量n,建立m个点。 第i条路线的虚拟源点ver=n+i,通过增加偏移量n,建立m个点。
然 后 再 n + m 个 点 的 图 内 跑 拓 扑 排 序 , 再 跑 最 长 路 。 然后再n+m个点的图内跑拓扑排序,再跑最长路。 然后再n+m个点的图内跑拓扑排序,再跑最长路。
输 出 m a x 1 ≤ i ≤ n d i s [ i ] 即 可 。 输出max_{1≤i≤n}dis[i]即可。 输出max1≤i≤ndis[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;
}