NOIP2013 普及组 车站分级

题意

一条单向的铁路线上,依次有编号为 1, 2, …, n 的 n 个火车站。每个火车站都有一个级别,最低为 1 级。现有若干趟车次在这条线路上行驶,每一趟都满足如下要求:如果这趟车次停靠了火车站 x,则始发站、终点站之间所有级别大于等于火车站 x 的都必须停靠。
(注意:起始站和终点站自然也算作事先已知需要停靠的站点)
例如,下表是 5 趟车次的运行情况。其中,前 4 趟车次均满足要求,而第 5 趟车次由于停靠了 3 号火车站(2 级)却未停靠途经的 6 号火车站(亦为 2 级)而不满足要求。
NOIP2013 普及组 车站分级_第1张图片
现有 m 趟车次的运行情况(全部满足要求),试推算这 n 个火车站至少分为几个不同的级别。



[Solution]
这题可以根据车分级,也可以根据点分级,写起来都差不多
(只是根据车分级跑的快,根据点分级跑得慢……)

先考虑点:
先说如何分级:如果两个点(A和B)被同一辆车经过,并且车停在了A,没有停在B,那么A的等级一定比B高.

那么根据这个,把等级高的向等级低的连一条边,表示它们不能在同一个等级.这个做法还是很好实现的,而且代码也很短.只需在读入时记录停留的点和没停留的点,把停留的点向所有没停留的点连一条边就好了.

这就是根据点分级的做法了,只要算出不同等级的数量,就得到了答案

然后就是怎么求的问题了,很容易发现,构造出的图一定是一个有向无环图(DAG),所以当然是选择dp求了.每个点的等级都是所有儿子中的最大值加1.

由于数据很水,这种方法卡下常数就过了…..

如果有了点的做法,其实车的做法也很接近了:
把车的等级定为这辆车上等级最低的点.对于两辆车(A,B),假如它们的路线相交,在相交路径上停靠点少的车的等级比另一辆高.这样车也有等级了,然后把车当成点,再按点的算法计算就好了.

但是这样好像不对,车的等级为什么能代表点的等级呢?

可以看下面的图:
NOIP2013 普及组 车站分级_第2张图片
(黑点表示停靠的点)

A车的等级为2,B为1.但是它们上面都有和它们等级不同的点;不过,这也没关系,因为取了这辆车中等级最高的点,所以不用关心其它点.
(但是必须再加一辆从1到n停靠所有点的车,保证没有点的等级为0

这样的话和上一种其实差不多,但是,车之间比较大小只需要用前缀和维护区间和,就可以让比较大小的复杂度变为 O(1) ,但是点之间比较大小的复杂度必须要 O(n)

所以,想到一种不怎么样的算法后,再仔细想想,可能就想出整解了

#include 
#include 
#include 
#include 
using namespace std;
const int M=1005;
struct node {
    int st,ed;
    int sum[M];
    bool operator > (const node &t) const {
        int mst=max(st,t.st);
        int med=min(ed,t.ed);
        if (mst>med) return false;
        return sum[med]-sum[mst-1]>t.sum[med]-t.sum[mst-1];
    }
} way[M];
vector <int> e[M];
int dp[M];
int rec(int x) {
    if (dp[x]) return dp[x];
    int res=0;
    for (int i=0;ireturn dp[x]=res+1;
}
int main() {
    int n,m;
    cin>>n>>m;
    for (int i=1;i<=m;++i) {
        int s;
        scanf("%d",&s);
        for (int j=1;j<=s;++j) {
            int x;
            scanf("%d",&x);
            if (j==1) way[i].st=x;
            else if (j==s) way[i].ed=x;
            way[i].sum[x]=1;
        }
        for (int j=way[i].st;j<=way[i].ed;++j) way[i].sum[j]+=way[i].sum[j-1];
    }
    way[0].st=0,way[0].ed=n;
    for (int i=1;i<=n;++i) way[0].sum[i]=way[0].sum[i-1]+1;
    for (int i=0;i<=m;++i) {
        for (int j=0;j<=m;++j) {
            if (i==j) continue;
            if (way[i]>way[j]) e[i].push_back(j);
        }
    }
    int res=0;
    for (int i=0;i<=m;++i) {
        res=max(res,rec(i));
    }
    cout<return 0;
}

你可能感兴趣的:(dp,图论,NOIP,图论,拓扑排序)