车站分级 (2013noip普及组T4)(树形DP)

题目描述

一条单向的铁路线上,依次有编号为 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个火车站最少划分的级别数。

思路:

 

这么多巨佬在上,我这个蒟蒻也只能来一发树形DP了(其实好像是森林DP因为我懒得建超级根节点了

 

很多大佬都用了拓扑排序,这是个好思想,但我太弱了不会用

 

我的这个树形DP的连边和很多大佬很像,每一条线路分别处理

 

如果某个点在起点到终点范围内,且没有停靠,由题意可知,这几站一定比停靠站的等级低,我就从停靠的站向不停靠的站连边,表示大小关系,当然,我不连重边

 

然后我开始遍历一遍所有的点,如果这个点没有入边,就说明它可能是最大的,我从这个点跑树形DP,每上升一层,大小+1,我们用动规找到最大层数,就是这个根点的答案

 

当然,我要求的是全图层数,当然要写一个max来比较了。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#define rii register int i
#define rij register int j
using namespace std;
struct cz{
    int rd,cd,to[1005];
}x[1005];
int n,bj[1005],ans,tk[1005],bs,kkk,ltt,m,gh[1005][1005],as[1005],sf[1005];
void jb(int bh)
{
    x[bh].rd+=ltt;
    for(rii=1;i<=ltt;i++)
    {
        if(gh[bh][tk[i]]==0)
        {
            bs++;
            x[tk[i]].cd++;
            x[tk[i]].to[x[tk[i]].cd]=bh;
            gh[bh][tk[i]]=1;
        }
    }
}
void dplast(int wz)
{
    for(rii=1;i<=x[wz].cd;i++)
    {
        if(sf[x[wz].to[i]]==0)
        {
            dplast(x[wz].to[i]);
            sf[x[wz].to[i]]=1;
        }
        as[wz]=max(as[wz],as[x[wz].to[i]]+1);
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(rii=1;i<=m;i++)
    {
        int st,fi;
        memset(bj,0,sizeof(bj));
        scanf("%d",&ltt);
        for(rij=1;j<=ltt;j++)
        {
            scanf("%d",&kkk);
            tk[j]=kkk;
            bj[kkk]=1;
            if(j==1)
            {
                st=kkk;
            }
            if(j==ltt)
            {
                fi=kkk;
            }
        }
        for(rij=st+1;j<fi;j++)//建边
        {
            if(bj[j]==0)
            {
                jb(j);
            }
        }
    }
    for(rii=1;i<=n;i++)
    {
        if(x[i].cd==0)//初始化,如果是叶子节点,就不需要向下寻找,层数默认是1
        {
            as[i]=1;
            sf[i]=1;
        }
    }
    int maxn=0;
    for(rii=1;i<=n;i++)
    {
        if(x[i].rd==0)
        {
            dplast(i);
        }
    }
    for(rii=1;i<=n;i++)
    {
        maxn=max(maxn,as[i]);
    }
    cout<<maxn;
}

 

转载于:https://www.cnblogs.com/ztz11/p/9280160.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值