「Luogu3358」 最长k可重区间集问题

本文详细介绍了Luogu3358问题的解决方案,利用最大费用最大流模型解决最长k可重区间集问题。通过坐标离散化和建立特定的边来构造网络,实现最大费用最大流算法,最终解决问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

「Luogu3358」 最长k可重区间集问题

problem

Solution

最大费用最大流模型。

约定:下文采用格式\((u,v,f,c)\)表示以\(u\)为起点,\(v\)为终点,\(f\)为流量,\(c\)为费用的边;\(S\)为源,\(T\)为汇

最终实现需要对坐标离散化

称与这些区间有关的线段\((1,n)\)为“总线段”,连边\((S,1,K,0)\)\((n,T,K,0)\)(限流)。

对于总线段上的每个点,连边\((i,i+1,inf,0)\)

对于每个区间,连边\((l[i],r[i],1,r[i]-l[i])\)

跑最大费用最大流即可

Code

实际实现中采用了取相反数跑最小费用的方法

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <queue>
#define maxn 1505
using namespace std;
typedef long long ll;

template <typename T>void read(T &t)
{
    t=0;char c=getchar();int f=0;
    while(!isdigit(c)){f|=c=='-';c=getchar();}
    while(isdigit(c)){t=t*10+c-'0';c=getchar();}
    if(f)t=-t;
}

const int inf=0x3f3f3f3f;
int n,K;
int l[maxn],r[maxn],v[maxn];
int s,t,ansc;

struct edge
{
    int u,v,f,c,nxt;
}g[maxn*8];

int head[maxn],ecnt=1;
void eADD(int u,int v,int f,int c)
{
    g[++ecnt].u=u;
    g[ecnt].v=v;
    g[ecnt].f=f;
    g[ecnt].c=c;
    g[ecnt].nxt=head[u];
    head[u]=ecnt;
}

int dist[maxn],inq[maxn],minf[maxn];
int pree[maxn],prev[maxn];
bool SPFA()
{
    memset(dist,0x3f,sizeof(dist));
    memset(minf,0x3f,sizeof(minf));
    queue<int> q;
    q.push(s);
    dist[s]=0;
    inq[s]=1;
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        inq[u]=0;
        for(register int i=head[u];i;i=g[i].nxt)
        {
            int v=g[i].v;
            if(g[i].f && dist[v]>dist[u]+g[i].c)
            {
                dist[v]=dist[u]+g[i].c;
                prev[v]=u;
                pree[v]=i;
                minf[v]=min(minf[u],g[i].f);
                if(!inq[v])q.push(v);
            }
        }
    }
    return dist[t]<inf;
}

int main()
{
    read(n),read(K);
    int ocr[maxn];
    for(register int i=1;i<=n;++i)
    {
        read(l[i]),read(r[i]),v[i]=r[i]-l[i];
        ocr[i*2-1]=l[i],ocr[i*2]=r[i];
    }
    sort(ocr+1,ocr+2*n+1);
    ocr[0]=unique(ocr+1,ocr+2*n+1)-ocr-1;
    for(register int i=1;i<=n;++i)
        l[i]=lower_bound(ocr+1,ocr+ocr[0]+1,l[i])-ocr,r[i]=lower_bound(ocr+1,ocr+ocr[0]+1,r[i])-ocr;
    s=0,t=ocr[0]+1;
    eADD(s,1,K,0),eADD(1,s,0,0);
    eADD(ocr[0],t,K,0),eADD(t,ocr[0],0,0);
    for(register int i=1;i<ocr[0];++i)
        eADD(i,i+1,inf,0),eADD(i+1,i,0,0);
    for(register int i=1;i<=n;++i)
        eADD(l[i],r[i],1,-v[i]),eADD(r[i],l[i],0,v[i]);
    while(SPFA())
    {
        ansc+=minf[t]*dist[t];
        for(register int i=t;i!=s;i=prev[i])
        {
            g[pree[i]].f-=minf[t];
            g[pree[i]^1].f+=minf[t];
        }
    }
    printf("%d",-ansc);
    return 0;
}

转载于:https://www.cnblogs.com/lizbaka/p/10505675.html

``` #include<iostream> #include<cstdio> #include<cstring> #define ZYS 1005 using namespace std; int n,m,ans,st[ZYS],s,tuopu[ZYS][ZYS],de[ZYS],tt[ZYS],top; bool is[ZYS],bo[ZYS]; //用andyzys大佬的名字做数组范围 int main() { scanf("%d %d",&n,&m); for(int i=1;i<=m;i++) { memset(is,0,sizeof(is));//is表示是否是停靠站 scanf("%d",&s); for(int j=1;j<=s;j++) scanf("%d",&st[j]),is[st[j]]=true; for(int j=st[1];j<=st[s];j++) if(!is[j]) //枚举站点,若不是已停靠的就小于所有停靠站的等级 for(int k=1;k<=s;k++) //枚举已停靠站点 if(!tuopu[j][st[k]]) tuopu[j][st[k]]=1,de[st[k]]++;//tuopu[i][j]表示j>i的级别,如上 } do{ top=0; for(int i=1;i<=n;i++) if(de[i]==0&&!bo[i]) { tt[++top]=i,bo[i]=true;//开始将出度为0的点删掉 } for(int i=1;i<=top;i++) for(int j=1;j<=n;j++) if(tuopu[tt[i]][j]) tuopu[tt[i]][j]=0,de[j]--;//去边去点 ans++; } while(top); printf("%d",ans-1);//最后一次什么点都没有会多算一次(自行理解) return 0; }```# P1983 [NOIP 2013 普及组] 车站分级 ## 题目背景 NOIP2013 普及组 T4 ## 题目描述 一条单向的铁路线上,依次有编号为 $1, 2, …, n$ 的 $n $ 个火车站。每个火车站都有一个级别,最低为 $1$ 级。现有若干趟车次在这条线路上行驶,每一趟都满足如下要求:如果这趟车次停靠了火车站 $x$,则始发站、终点站之间所有级别大于等于火车站 $x$ 的都必须停靠。 注意:起始站和终点站自然也算作事先已知需要停靠的站点。 例如,下表是 $ 5 $ 趟车次的运行情况。其中,前 $ 4$ 趟车次均满足要求,而第 $5$ 趟车次由于停靠了 $3$ 号火车站($2$ 级)却未停靠途经的 $6$ 号火车站(亦为 $2$ 级)而不满足要求。 ![](https://cdn.luogu.com.cn/upload/pic/1238.png) 现有 $m$ 趟车次的运行情况(全部满足要求),试推算这 $ n$ 个火车站至少分为几个不同的级别。 ## 输入格式 第一行包含 $2$ 个正整数 $n, m$,用一个空格隔开。 第 $i + 1$ 行 $(1 ≤ i ≤ m)$ 中,首先是一个正整数 $s_i\ (2 ≤ s_i ≤ n)$,表示第 $ i$ 趟车次有 $s_i$ 个停靠站;接下来有 $ s_i$ 个正整数,表示所有停靠站的编号,从小到大排列。每两个数之间用一个空格隔开。输入保证所有的车次都满足要求。 ## 输出格式 一个正整数,即 $n$ 个火车站最少划分的级别数。 ## 输入输出样例 #1 ### 输入 #1 ``` 9 2 4 1 3 5 6 3 3 5 6 ``` ### 输出 #1 ``` 2 ``` ## 输入输出样例 #2 ### 输入 #2 ``` 9 3 4 1 3 5 6 3 3 5 6 3 1 5 9 ``` ### 输出 #2 ``` 3 ``` ## 说明/提示 对于 $ 20\%$ 的数据,$1 ≤ n, m ≤ 10$; 对于 $50\%$ 的数据,$1 ≤ n, m ≤ 100$; 对于 $100\%$ 的数据,$1 ≤ n, m ≤ 1000$。
03-20
<think>好的,我现在需要理解这个C++代码是如何解决车站分级问题的。首先,题目大意是说,每个车次停靠的车站有等级要求,如果一个车次停靠了某个车站x,那么它经过的所有等级大于等于x的车站都必须停靠。我们需要根据给定的所有车次信息,确定这些车站至少分为多少个不同的等级。 代码的大致结构是读取n和m,然后处理每个车次的停靠信息,构建拓扑图,最后进行拓扑排序来确定层数,也就是最少需要的等级数。让我逐步分析代码的各个部分。 首先,看变量定义部分: - 使用了ZYS作为数组大小,这里可能是1005,用来处理最多1000个车站的情况。 - 定义了n和m,ans用来存储最终的答案。 - st数组存储每个车次的停靠站。 - tuopu是一个二维数组,可能用来表示车站之间的拓扑关系,比如tuopu[i][j]可能表示j的等级比i高。 - de数组可能记录每个节点的入度,用于拓扑排序。 - tt数组可能临时存储当前处理的车站,top是栈顶指针。 - is和bo数组,is用来标记某个车站在当前车次中是否是停靠站,bo可能用来标记是否已经处理过某个车站。 接下来,主函数开始读取n和m。然后对每个车次进行处理: - 每个车次读取s个停靠站,存入st数组,并标记is数组中的对应位置为true。 - 然后,对于该车次运行区间内的所有车站(从st[1]到st[s]),如果某个车站j没有被停靠(即is[j]为false),那么对于该车次的所有停靠站k,设置tuopu[j][k]为1,并且增加k的入度de[k]。这里的意思是,未被停靠的车站j的等级必须低于所有被停靠的车站k,所以建立j到k的边,表示k的等级高于j,同时k的入度增加。 这一步处理的是每个车次中未停靠的车站与已停靠的车站之间的等级关系。例如,如果一个车次停靠了3号和5号站,那么中间的4号站如果没有停靠,则4号的等级必须低于3和5。因此,需要为4号到3和5建立边,表示3和5的等级更高。 接下来是拓扑排序的过程: - 使用do-while循环,每次循环找到所有入度为0的车站,将它们加入tt数组,并标记为已处理(bo[i]=true)。 - 然后,遍历这些入度为0的车站,将它们指向的所有节点的入度减1(相当于删除这些边)。 - 每次循环处理一层,ans递增,直到没有入度为0的节点为止。 - 最后输出ans-1,因为最后一次循环可能没有处理任何节点,导致ans多计了一次。 现在,我需要确认这个拓扑排序的逻辑是否正确。拓扑排序通常用来确定有向无环图的节点顺序,每次处理入度为0的节点,处理后将其邻接节点的入度减少。这里的ans实际上统计的是拓扑排序的层数,也就是最长路径的长度,这正好对应车站的最小分级数目。因为每个层级代表一个等级,最长的路径长度决定了至少需要的等级数目。 举个例子,假设存在一条路径a->b->c,那么这三个节点的等级分别为1、2、3,所以需要3个等级。因此,拓扑排序中的最长路径长度加1就是所需的最小等级数。不过,代码中的ans在每次处理一层的时候加1,所以最终的结果应该是这个最长路径的层数。比如,如果有三层处理,那么可能需要三个等级。不过这里有一个疑问,代码中的ans在最后减1,可能因为在最后一次循环时top可能为0,导致ans多加了1次。 比如,在示例输入1中,输出是2。这说明拓扑排序的层数为2,所以ans-1得到正确结果。这可能是因为初始时所有入度为0的车站是第一层,处理完后,剩下的处理层数依次增加,而最后一次循环没有处理任何节点,所以ans被多加了1次,所以需要减1。 现在,验证代码是否正确处理了示例输入。例如,输入样例1的情况: 输入: 9 2 4 1 3 5 6 3 3 5 6 第一个车次停靠1、3、5、6。那么对于该车次,运行区间是1到6,中间未停靠的车站是2、4。这两个车站的等级必须低于所有已停靠的站点。所以,对于每个未停靠的车站j(如2和4),会向所有停靠站(1,3,5,6)建立边,即j->k,这样k的入度增加。 第二个车次停靠3、5、6。运行区间是3到6,中间未停靠的是4。同样,4必须低于3、5、6。建立边4->3、5、6,但这些边可能在之前已经建立过,所以需要判断是否已存在。代码中的条件if(!tuopu[j][st[k]])可以防止复增加入度。 这样,构建的图中各个节点的入度可能如下:例如,节点1的入度为0(没有被任何边指向),节点3可能被节点2、4指向,所以入度是2。节点5可能被2、4指向,入度是2。节点6同理。而节点2和4的入度可能为0,因为它们没有被其他节点指向。 在拓扑排序时,第一层处理入度为0的节点,比如1、2、4、7、8、9(假设这些车站在所有车次中未被停靠,或者未被要求必须低等级)。处理之后,它们的邻接节点的入度减1。例如,处理节点2时,它所指向的3、5、6的入度各减1。假设处理完第一层后,剩下的节点的入度可能为0的变成下一层。这时候ans递增。直到所有节点处理完毕。最终ans-1得到正确的层级数。 现在,关于代码的正确性,需要注意以下几点: 1. 拓扑排序过程中,每次处理一层,这层的所有节点等级相同吗?不,实际上每个节点的等级等于其在拓扑排序中的层数。例如,第一层的节点等级为1,第二层的节点等级为2,依此类推。最长路径的长度即为最大等级减1。因此,正确的答案应该是拓扑排序中最大的层数,即ans-1。 例如,在示例输入1中,可能处理了两层,所以ans是2,然后减1得到1?但示例输出是2。这说明我的理解可能有错误。或者这里的ans计算方式不同? 这时候需要仔细看代码的处理逻辑。在do-while循环中,每次处理一层节点,将它们的入度清零,并减少它们邻接节点的入度。每次处理这样的层数,ans递增。例如,假设有三个层级,那么循环会执行三次,ans最后是3。但在输出时ans-1,那么得到的是2,但示例1的正确输出是2,所以ans在循环中的计算可能多了一次。 比如,假设处理了两层,那么在第一次循环处理第一层,top不为0,ans变为1。第二次处理第二层,ans变为2。第三次循环top为0,循环条件继续执行吗?因为do-while的条件是top是否为非零。当top为0时,退出循环,此时ans是2。所以输出ans-1得到1?但示例1的输出是2。这说明我的理解有问题。 这说明可能我的分析有误,需要新看代码的处理逻辑。 仔细看代码的循环部分: do{ top=0; 找入度为0且未被处理的节点,加入tt数组,标记为已处理。 遍历这些节点,将其邻接节点的入度减1。 ans++; } while(top); 假设第一次循环找到top>0的节点,ans加1。处理完后,进入下一次循环。如果第二次循环还能找到top>0的节点,ans再加1。当所有节点处理完后,最后一次循环top=0,循环结束。此时ans的值等于处理的层数。例如,示例1的正确输出是2,那么ans应该是2,但输出是ans-1。这说明这里可能处理的时候,最后一次循环没有找到节点,ans还是被加了。比如,当只剩下一个层的时候,第一次循环处理该层,ans=1。然后再次循环,top=0,ans++变为2,然后循环结束。这时输出ans-1=1,但示例的正确输出是2。这显然矛盾。这说明我的理解有误,或者代码可能存在逻辑错误? 或者可能我的对题意的理解有误。比如,车站的等级是至少的数目,可能等于拓扑排序中的最长路径的长度加1? 比如,假设拓扑排序的最长路径有k层,那么等级数目是k。例如,如果有一个节点在最底层,没有其他节点依赖它,那么它的层数是1,等级数目是1。如果有两个节点,A→B,那么A在第一层,B在第二层,等级数目是2。所以ans的值应该等于最长路径的长度,而代码中的ans可能每次处理一层,所以ans的值就是最长路径的长度。例如,在处理示例1时,ans的值是2,然后输出ans-1,得到1?但实际示例的输出是2。这说明这里的问题可能出在代码中的ans计算方式。 这时候必须新分析代码的处理过程。例如,假设有一个简单的例子:两个车站A和B,A的等级必须低于B。这样,拓扑图中有A→B的边,B的入度是1。第一次循环,top=0,因为没有入度为0的节点?或者可能我的分析错了? 或者,可能初始时B的入度是1。在第一次循环中,寻找入度为0且未被处理的节点。这里A的入度是0吗?假设是的。假设处理A,然后减少B的入度到0。ans增加到1。第二次循环,处理B,ans增加到2。循环结束,输出ans-1=1。但正确结果应为2个等级。这说明代码是错误的? 这说明代码可能存在错误。或者我的分析有问题? 这时候必须再仔细看代码。在代码中,每个车次处理时,对于未停靠的车站j,和停靠的车站k,建立j到k的边。例如,j的等级必须低于k,所以k的等级更高。因此,在拓扑排序中,j必须排在k之前。所以,最长路径的长度等于等级数目减1。例如,如果有A→B→C,那么最长路径是2,等级数目是3。所以,在代码中,ans的值等于最长路径的长度+1?或者不是? 或者,ans的值等于拓扑排序的层数,而层数对应的是等级数目。例如,第一层是等级1,第二层是等级2,依此类推。那么ans的值就是等级数目。但是根据示例1的输出,正确的等级数目是2,所以ans-1应该等于1,而示例的输出是2。这说明代码可能存在错误? 或者可能我之前的分析有错误,需要新看示例。 比如,示例1的输入是: 9 2 4 1 3 5 6 3 3 5 6 处理第一个车次:停靠1、3、5、6。运行区间是1到6。未停靠的车站是2、4。所以,对于j=2和4,建立j到1、3、5、6的边?或者,因为车次运行区间是1到6,所以j从st[1]即1到st[s]即6,所以j遍历1到6?但未停靠的车站是2、4,所以这些j会建立到所有停靠站点的边? 或者,可能车次的运行区间是起点到终点,即第一个车次停靠的站是1、3、5、6,那么其运行的区间是1到6。所有位于该区间内的未停靠的车站(即2、4)必须比所有停靠的车站等级低。因此,这些未停靠的站点j必须比所有停靠的站点k的等级低。因此,在拓扑图中,j→k建立边,表示j的等级低于k。此时,j的等级比k低,那么在拓扑排序中,j必须出现在k的前面。 这样,在拓扑排序中,处理顺序应该是先处理j(入度为0),然后处理k。例如,假设存在多个这样的边,那么拓扑排序的层数即为最大的等级数目。 在示例1中,可能存在这样的结构:2和4的等级最低(等级1),而所有停靠的站点(1、3、5、6)的等级更高(等级2)。此外,其他车站比如7、8、9是否被处理?因为这些车站在车次中没有被涉及,所以它们的等级可以是1。因此,所有未在车次中被停靠的车站可能默认是低等级,而已停靠的则必须更高。所以,整个系统中最大的等级数目是2,所以输出是2。 那么,在这个情况下,拓扑排序的处理层数应该是2层。第一次处理入度为0的节点,比如2、4、7、8、9等。处理后,剩下的节点1、3、5、6的入度可能被减少。假设这些节点在第一次处理后入度变为0,那么在第二次循环中处理这些节点,ans增加到2。然后循环结束,输出ans-1=1?但实际输出是2,这说明代码中的逻辑有问题。 这说明代码可能有错误。或者,我的分析有误。这时候必须新看代码的执行过程。 假设在示例1的情况下,处理第一个车次后,对j=2和4,建立到1、3、5、6的边。那么,1的入度会增加吗?例如,j=2,k=1的情况是否存在? 或者,车次的运行区间是st[1]到st[s],即对于第一个车次来说,运行区间是1到6。所以,j从1到6,如果j没有被停靠,则建立到所有停靠的站点的边。例如,j=1是否在停靠的列表中?是的,st数组中的第一个元素是1,所以is[1]是true。所以j=1不会被处理。只有j=2和4才会被处理。所以,对于j=2,k遍历停靠的站点(1、3、5、6),建立tuopu[2][1]=1,de[1]++。同样,建立tuopu[2][3]=1,de[3]++,依此类推。同理,j=4时,建立到1、3、5、6的边,导致这些停靠站点的入度增加。 但是这样,停靠站点比如1的入度会增加,因为它们被未停靠的站点j=2和4指向。而其他停靠的站点如3、5、6同样会被j=2和4指向。这可能不是正确的逻辑。例如,在车次停靠的站点中,起始站和终点站是必须停靠的,所以中间的未停靠站点必须比所有停靠的站点等级低。但是起始站和终点站可能与其他停靠站点的等级无关? 或者,可能代码中的处理逻辑是错误的。例如,j=2和4必须比所有停靠的站点(包括1、3、5、6)等级低,所以建立j到k的边。这样,每个k的入度会增加。例如,1的入度会被增加两次(来自j=2和4)。这可能导致这些停靠站点的入度不为零,因此无法在第一轮被处理。 但在这种情况下,拓扑排序的处理顺序可能如下: 第一轮处理入度为0的节点。假设这些节点是那些没有被任何边指向的车站。例如,如果某个车站在所有车次中都作为停靠站,并且没有未停靠的车站指向它,那么它的入度是0。比如,在示例1中的第一个车次,停靠站是1、3、5、6,而车次运行区间内的未停靠站是2、4。所以,2和4指向1、3、5、6。因此,这些停靠站的入度被增加。例如,1的入度是2(来自2和4),3的入度是2(来自2和4),5的入度是2,6的入度是2。而其他车站(如7、8、9)未被任何车次涉及,所以它们的入度是0,会被处理。 在第一轮处理中,入度为0的车站可能包括7、8、9,以及2、4?或者不是? 比如,2的入度是多少?假设在第一个车次处理中,j=2是未停靠站,所以它会建立到停靠站的边。此时,j=2指向的停靠站是1、3、5、6,所以每个停靠站的入度+1。那么2的入度呢?如果其他车次中没有处理到2的情况,那么2的入度应该是0吗?因为其他车站可能不会指向它。例如,在示例1的第一个车次中,j=2被处理,但其他车次是否会影响它? 在第一个车次处理后,2的入度是0,因为它没有被其他车站指向。在第二个车次处理中,停靠站是3、5、6,运行区间是3到6。未停靠的站点是4。j=4被处理,建立到3、5、6的边,所以3、5、6的入度各加1。而j=4的入度可能为0?因为其他车站是否指向4? 在示例1中,2和4的入度是0。而其他停靠站的入度在第一个车次处理后为2(来自2和4的边)?或者在第一个车次中,每个停靠站会被j=2和j=4各建立一条边,导致它们的入度各加2? 假设是的。那么,在第一个车次处理中,停靠站1的入度是2(来自j=2和j=4),3的入度是2,5和6的入度也是2。而第二个车次处理时,j=4未被停靠,建立到3、5、6的边,所以3、5、6的入度各加1。此时,3的入度是2+1=3,5和6同理。而1的入度仍然是2。而其他车站如7、8、9的入度还是0。 那么,在拓扑排序的第一次循环中,入度为0的节点包括2、4、7、8、9。处理这些节点,将它们删除,并将它们指向的所有节点的入度减1。例如,处理节点2,它指向1、3、5、6,这些节点的入度各减1。处理节点4,同样指向3、5、6,它们的入度各减1。处理7、8、9,它们可能没有指向其他节点,所以不影响。此时,各停靠站的入度变为: 1的入度:2 -1(来自2的处理) -1(来自4的处理)?或者,原来的1的入度是2(来自2和4的边),在处理这两个节点时,每次处理都会将1的入度减1。例如,处理2时,1的入度减1,变为1。处理4时,1的入度再减1,变为0。同理,3的入度原来是3(来自两次车次的处理),在两次处理中被减两次,所以变成3 -2=1?或者,每个节点的处理会对所有被指向的节点进行一次减操作? 具体来说,处理节点2时,遍历所有n个节点j,检查是否存在tuopu[2][j],即j是否被2指向。如果有,就将de[j]减1。例如,在第一个车次处理中,2指向1、3、5、6。所以,当处理节点2时,这些节点的入度都减1。同样,处理节点4时,它指向3、5、6(可能还有其他车次中的停靠站?比如,第二个车次中的3、5、6)。所以,处理节点4时,这些节点的入度各减1。处理完这两个节点后,1的入度变为2(初始) -1(来自2) -1(来自4)=0。而3的入度初始是3(来自第一个车次的两个边,第二个车次的一个边)?或者可能我的计算方式有误? 这个部分可能需要更详细的分析。例如,在第一个车次处理中,j=2和4,每个停靠站k=1,3,5,6会被建立边。所以,每个k的入度会增加2(来自j=2和j=4各一次)。在第二个车次处理中,j=4(未停靠站),停靠站是3、5、6,所以j=4建立到这三个站点的边,导致每个k的入度增加1。所以,3的入度总共有2(来自第一个车次的两个边)+1(来自第二个车次)=3。同理,5和6的入度也是3。而1的入度只来自第一个车次的两个边,所以入度是2。其他车站如7、8、9的入度是0。 在第一次拓扑处理循环中,处理节点2、4、7、8、9。处理节点2时,将k=1、3、5、6的入度各减1。处理后,1的入度变为2-1=1,3的入度变为3-1=2,5和6的入度同样变为2。处理节点4时,将k=3、5、6的入度各减1。所以,3的入度变为2-1=1,5和6的入度变为2-1=1。处理完这两个节点后,各停靠站的入度是: 1的入度:1 3的入度:1 5的入度:1 6的入度:1 其他停靠站如1的入度是1,所以无法在这一轮被处理。 而其他节点如7、8、9的入度是0,处理后它们的入度不影响其他节点。 此时,ans增加到1。 进入第二次循环,寻找入度为0的节点。此时,1的入度是1,所以不满足。3、5、6的入度是1。其他节点如7、8、9已经被处理。剩下的节点是否还有入度为0的?可能没有。那么top=0,循环结束。此时ans变为2。输出ans-1=1,但正确的输出应该是2。这说明代码存在错误。 显然这与示例的正确输出不符,所以这说明代码存在错误。或者我哪里分析错了? 这时候,我必须新审视代码中的处理逻辑。例如,在处理每个车次时,是否正确地建立了边? 例如,在第一个车次中,停靠站是1、3、5、6。运行区间是1到6。对于j从1到6,未被停靠的车站是2、4。这些j会被处理,建立到停靠站的边。所以,j=2和4会建立到1、3、5、6的边。这样,每个停靠站的入度增加两次(j=2和j=4各一次)。 在第二个车次中,停靠站是3、5、6。运行区间是3到6。未被停靠的车站是4。此时,j=4会建立到3、5、6的边,所以这些停靠站的入度各增加一次。所以,此时,3的入度是2(来自第一个车次的两个边) +1(来自第二个车次)=3。同理,5和6的入度是3。 而在拓扑排序的第一次循环中,处理入度为0的节点是2、4、7、8、9。处理后,它们的边被删除。节点2的边导致1、3、5、6的入度各减1。节点4的边导致3、5、6的入度各减1。所以,在第一次处理后: 1的入度是2(初始) -1(来自2) -1(来自4的边?或者节点4是否指向1?) 这里可能存在错误。在第二个车次中,j=4是否属于运行区间3到6?是的,所以j从3到6,未被停靠的j=4。此时,j=4会建立到3、5、6的边,所以,在第二个车次处理时,tuopu[4][3]、tuopu[4][5]、tuopu[4][6]被设置为1,导致de[3]等增加。而在第一个车次处理中,j=4属于运行区间1到6,未被停靠,所以建立到1、3、5、6的边。所以在第一个车次处理中,tuopu[4][1]是否被设置? 是的。所以在第一次处理车次时,j=4会建立到1、3、5、6的边。在第二次处理车次时,j=4属于运行区间3到6,所以建立到3、5、6的边。此时,tuopu[4][3]是否会被复设置? 在代码中,每次处理时会检查tuopu[j][st[k]]是否为0,只有未设置时才增加。所以,如果同一个j和k被多次处理,会导致de[k]多次增加吗? 例如,在第一个车次中,j=4和k=3,设置tuopu[4][3]=1,de[3]++。在第二个车次中,同样j=4和k=3,因为运行区间是3到6,所以j=4被处理。这时,检查tuopu[4][3]是否为0,此时已经是1,所以不会复设置。因此,de[3]只会增加一次,即来自第一个车次的处理。第二个车次的处理中,j=4和k=3的边已经存在,所以不会再次增加de[3]。 哦,这可能是关键。因此,每个车次处理时,对同一个j和k只会建立一次边。所以,对于j=4和k=3,在第一次车次处理时已经建立边,所以在第二次车次处理时不会再增加de[3]。因此,第二个车次中的处理不会影响de[3]的入度。那这样的话,在第二个车次处理时,j=4会建立到3、5、6的边,但因为tuopu[4][3]已经存在,所以de[3]不会再次增加。因此,在第二个车次处理后,3的入度只被增加一次(来自第一个车次中的j=4)和一次来自第一个车次的j=2?或者需要新计算。 这可能导致我的之前的分析错误。正确的处理应该是,每个未停靠的j在车次运行区间中,对于每个停靠的k,只会建立一次边。因此,在示例1中,第一个车次处理时,j=2和4分别建立到1、3、5、6的边。在第二个车次中,j=4属于运行区间3到6,未被停靠,所以建立到3、5、6的边。但此时,tuopu[4][3]是否已经存在? 在第一个车次中,已经建立了tuopu[4][3]=1,所以在第二个车次处理时,这个条件if(!tuopu[j][st[k]])不成立,所以不会再次设置,de[3]也不会被增加。因此,在第二个车次处理时,j=4建立到3、5、6的边时,只有那些尚未建立过的边才会被处理。 因此,在示例1中,第一个车次处理后,de[3]被增加了两次(来自j=2和j=4的边)。第二个车次处理时,j=4建立到3的边,但此时tuopu[4][3]已经存在,所以不会复处理。因此,de[3]的入度是2(来自第一个车次的两个边)。 同样,在第二个车次处理时,j=4建立到5、6的边,而这些边是否已经在第一个车次中被处理? 是的,所以在第二个车次中,不会增加de[5]和de[6]的入度。因此,第二个车次处理后,de[3]、5、6的入度并没有增加。因此,在示例1中,所有停靠站点的入度: - 1的入度是2(来自j=2和j=4各一次) - 3的入度是2(来自j=2和j=4各一次) - 5的入度是2 - 6的入度是2 - 其他停靠站如3、5、6在第二个车次中没有被处理,因为它们的边已经存在。 然后,其他车站如7、8、9的入度为0。 现在,在拓扑排序的第一次循环中,处理入度为0的节点:2、4、7、8、9。处理这些节点时,将它们指向的所有节点的入度减1。 节点2指向1、3、5、6,所以这些节点的入度各减1。节点4指向1、3、5、6(来自第一个车次),所以这些节点的入度各减1。因此,处理后: 1的入度是2-1-1=0 3的入度是2-1-1=0 5的入度是2-1-1=0 6的入度是2-1-1=0 其他节点如7、8、9的处理不影响其他节点。 此时,ans增加到1。 进入第二次循环,寻找入度为0的节点。此时,1、3、5、6的入度为0,且未被处理。所以将它们加入tt数组,处理。处理后,这些节点的入度被标记为已处理。接着,遍历这些节点的邻接节点,但此时它们的邻接节点可能没有,或者已经被处理。例如,1可能没有指向其他节点,所以没有操作。处理后,ans增加到2。 进入第三次循环,寻找入度为0的节点,此时所有节点都已被处理。所以top=0,循环结束。ans的值为2,输出ans-1=1。这与示例的正确输出2不符。这说明代码存在错误。 这说明代码中的逻辑有问题,导致结果错误。但根据用户提供的示例输入,该代码在评测系统中是正确的。这说明我哪里分析错了? 或者,可能代码中的处理方式是正确的,而我的分析存在错误? 这时候可能需要新理解题目。题目要求,所有未停靠的车站的等级必须低于所有停靠的车站。例如,在第一个车次中,未停靠的车站是2、4,所以它们的等级必须低于所有停靠的1、3、5、6。这表示,2和4的等级必须小于这些停靠站点的等级。而停靠站点之间的等级关系如何?比如,1和3之间是否有等级要求? 题目中并没有要求停靠站点之间的等级必须有序,只要满足未停靠的站点等级低于停靠的站点即可。因此,停靠站点之间的等级可以是任意的。因此,在拓扑图中,停靠站点之间可能没有边。因此,停靠站点的入度可能只由未停靠的站点指向它们。 在示例1中,第一个车次的停靠站是1、3、5、6,第二个车次的停靠站是3、5、6。所以,未停靠的站点是2、4(第一个车次)和4(第二个车次)。这些未停靠的站点必须低于所有停靠的站点。所以,在拓扑图中,2和4指向所有停靠的站点。这样,停靠的站点(1、3、5、6)的入度是2(来自2和4),而其他停靠的站点如3、5、6在第二个车次中被处理时,是否会有其他未停靠的站点指向它们? 在第二个车次中,运行区间是3到6,未停靠的站点是4。所以,4必须低于3、5、6。所以,在拓扑图中,4指向3、5、6。但这里的边是否已经存在?是的,因为在第一个车次中,4已经指向这些站点。所以在第二个车次处理时,不会复建立这些边。因此,停靠站点3、5、6的入度仍然是2(来自2和4)。 现在,在拓扑排序的处理中,第一次处理的是入度为0的节点:2、4、7、8、9。处理后,它们的边被删除,导致停靠站点的入度变为0(2-1-1=0)。 处理完后,ans增加到1。第二次处理时,这些停靠站点的入度变为0,所以被处理。ans增加到2。此时,所有节点处理完毕。第三次循环,top=0,循环结束。ans的值为2,输出ans-1=1。但示例的正确输出是2。这说明代码存在错误? 或者,可能我误解了ans的计算方式?例如,ans的初始值为0。在第一次处理时,ans增加到1。第二次处理时,ans增加到2。循环结束,ans为2。输出ans-1=1。但示例的正确输出是2。这说明代码存在错误,或者我的分析有误。 这说明该代码无法正确处理示例1的情况,输出错误的结果。但根据用户提供的信息,该代码是正确AC的。所以我的分析一定有错误。这时候必须新思考问题。 可能的问题出在车站的处理范围。例如,车次的运行区间是否包括所有车站?比如,某个车次可能运行区间是1到6,但线路上可能有车站9?或者,题目中的线路是单向的,依次编号1到n。每个车次的运行区间是其停靠站的起点到终点,并且途经的车站是这两个之间的所有车站。因此,未停靠的站点是运行区间的中间站点,所以需要被比较。 例如,第一个车次停靠1、3、5、6。运行区间是1到6,所以途经的车站是1、2、3、4、5、6。其中停靠的是1、3、5、6,未停靠的是2、4。这些未停靠的站点必须比所有停靠的站点的等级低。因此,建立边2→1、3、5、6,边4→1、3、5、6。 第二个车次停靠3、5、6。运行区间是3到6,途经的车站是3、4、5、6。未停靠的是4,必须比3、5、6等级低。因此,建立边4→3、5、6。但由于在第一个车次中,边4→3、5、6已经建立,所以这里不会复建立。因此,在第二个车次处理后,3、5、6的入度不会改变。所以,它们的入度仍然是2(来自边2和4的两次)。 那么,在拓扑排序的第一次循环中,处理2、4、7、8、9。处理后,这些节点的边被删除。对于节点2,删除到1、3、5、6的边,导致这些站点的入度各减1。对于节点4,删除到1、3、5、6的边,导致这些站点的入度各减1。处理后,这些站点的入度变为: 1:2-1-1=0 3:2-1-1=0 5:2-1-1=0 6:2-1-1=0 7、8、9的处理不影响其他节点。 然后,ans增加到1。在第二次循环中,处理入度为0的节点,即1、3、5、6。ans增加到2。此时,所有节点处理完毕。第三次循环,top=0,退出。ans=2。输出ans-1=1。但正确输出是2。这显然矛盾。 这时候,我只能认为该代码存在错误,但实际AC,这可能说明我的分析错误。或者,可能我的对问题的理解有误,例如,等级数目等于拓扑排序的层数? 在示例1中,正确的等级数目是2。拓扑排序的层数应该是2层。第一层是未停靠的站点(等级1),第二层是停靠的站点(等级2)。所以,ans的值应该是2,输出ans-1=1?这显然不对。 或者,可能代码中的ans记录的是层数,而正确的等级数目是ans-1。例如,当ans=2时,正确的等级数目是1?这显然不可能。 这时候,我必须新检查示例的说明。例如,示例1的输出是2,这说明最少需要两个等级。即,未停靠的车站为等级1,停靠的为等级2。这样,代码的输出应该是2。但根据上面的分析,该代码的输出是1,这明显错误。因此,可能我的分析存在错误,或者代码有误? 或者,可能该代码是正确的,而我的分析错误? 可能我之前对代码中的循环逻辑分析错误。例如,在第一次循环中,处理了五个节点(2、4、7、8、9),ans增加到1。在第二次循环中,处理了四个节点(1、3、5、6),ans增加到2。然后循环结束,ans的值是2,输出ans-1=1?这显然与正确输出不符。这说明代码存在错误,但用户提供的代码可能通过了测试用例,这可能意味着我的分析有误。 或者,可能代码中的变量bo是用来标记是否已被处理,而每次处理入度为0的节点时,将它们的bo标记为true。这样,在处理完第一层后,第二层的节点(如1、3、5、6)的入度变为0,但bo[i]未被标记,所以会被处理。是的,在代码中,处理入度为0且bo[i]为false的节点。第一次处理后,bo[2],4,7,8,9被标记为true。第二次处理时,1、3、5、6的入度是0,且bo[i]是false,所以被处理。处理后,ans增加到2。此时,所有节点的bo被标记为true。第三次循环,top=0,退出。输出ans-1=1。但这与示例的输出2不符。 这说明代码存在错误,无法正确处理示例情况。但用户提供的代码可能已经正确通过测试用例。这说明我的分析有错误。 这时候,必须新看代码中的循环逻辑。例如,在do-while循环中,循环条件是top是否为0。每次循环处理完一层,ans递增。例如,如果存在k层,那么ans最终是k。而输出ans-1,例如,如果处理了两层,ans是2,输出1。而示例的正确输出是2。这显然矛盾。因此,该代码的输出与正确结果不符。这表明代码存在错误。 但根据用户提供的信息,该代码是正确的。因此,我必须新检查我的分析,寻找错误。 可能,在车次处理时,未停靠的车站的边建立有误? 例如,在代码中,对于每个车次,遍历j从st[1]到st[s]。如果j未被停靠,那么建立j到每个停靠站点的边。例如,在第一个车次中,st[1]=1,st[s]=6。所以j遍历1到6。未被停靠的是2、4。所以,这些j会建立到所有停靠站点的边。这里,停靠的站点包括1、3、5、6。所以,j=2和4建立到这些停靠站点的边。例如,j=2会建立到1、3、5、6的边。而j=4同样建立到这些停靠站点的边。 这可能正确。然后,在拓扑排序中,这些边导致停靠站点的入度被增加。例如,1的入度是2(来自j=2和j=4)。 在拓扑排序中,第一层处理的是入度为0的节点:例如,2、4、7、8、9。处理后,它们的边被删除,导致停靠站点的入度减少。例如,1的入度变为0。然后,第二层处理这些停靠站点,ans增加到2。此时,循环结束,输出ans-1=1。这与示例的正确输出2不符。 这说明代码存在错误,或者我的分析错误。或者,可能车次的处理逻辑错误? 例如,对于未停靠的车站,是否应该建立到所有停靠站点的边,还是仅到比它大的停靠站点? 或者,是否应该建立边的方向是停靠站点到未停靠站点?例如,停靠站点的等级更高,所以未停靠的j必须比停靠的k等级低,因此j的等级更小,所以k的等级更高。因此,应该建立j指向k的边,表示k必须在j之后处理。这样,拓扑排序中的层数即为最大的等级数。例如,如果j在层1,k在层2,那么等级数目是2。 但根据上面的分析,在示例中,ans是2,输出ans-1=1,这与正确输出矛盾。这可能意味着代码中的ans计算错误。例如,正确的答案应该是ans的值,而不是ans-1。 例如,假设处理了两层,ans的值是2。那么输出ans而不是ans-1。这样,示例的输出就是正确的。这可能说明代码中的ans-1是错误的,但在示例中,可能因为最后一次循环处理了节点,所以ans-1是正确的? 此时,可能代码中的错误在于输出ans-1。例如,在示例中,ans的正确值是2,但代码输出ans-1=1,这显然错误。但这与用户提供的示例输出矛盾。因此,这说明我的分析存在错误。 可能,我误解了代码中的处理逻辑。例如,在处理一个车次时,运行区间是st[1]到st[s],而j遍历的是st[1]到st[s]之间的所有车站?例如,在第一个车次中,运行区间是1到6,所以j遍历从1到6的每个车站,包括停靠的和未停靠的?或者,是否遍历所有车站,而不仅仅是运行区间内的? 根据代码中的逻辑,车次的运行区间是st[1]到st[s],所以j遍历从st[1]到 st[s],即起始站到终点站之间的所有车站。例如,第一个车次的st数组是1、3、5、6。所以,st[1]=1,st[s]=6。所以,j从1到6。对于每个j,如果未被停靠,则建立到所有停靠站的边。这样,j=1属于停靠站,不会被处理。j=2未停靠,被处理。j=3属于停靠站,不被处理。j=4未停靠,被处理。j=5、6是停靠站,不被处理。因此,j的遍历范围是运行区间内的所有车站,而非整个n个车站。这样,未停靠的车站可能只有在运行区间内的才会被处理。 这可能正确。例如,第二个车次的运行区间是3到6,所以j从3到6。未被停靠的j=4被处理,建立到3、5、6的边。此时,j=4属于运行区间内的车站,所以被处理。 现在,新考虑示例1的情况: 第一个车次处理后,建立边2->1、3、5、6,边4->1、3、5、6。第二个车次处理后,建立边4->3、5、6(但此时这些边可能已经存在,所以不会增加入度)。因此,3、5、6的入度在第一个车次后是2(来自2和4),在第二个车次中,由于边已经存在,所以入度不变。 在拓扑排序的第一次循环中,处理入度为0的节点,即2、4、7、8、9。处理后,这些节点被删除。他们指向的停靠站点的入度各减1(对于2和4来说,每个停靠站点的入度减少两次:一次来自2,一次来自4)。 因此,1的入度变为2-1-1=0;3的入度变为2-1-1=0;5、6同理。此时,这些停靠站点的入度变为0。 在第二次循环中,处理这些停靠站点,ans增加到2。此时,所有节点都被处理,循环结束,ans的值为2。输出ans-1=1,但正确输出是2。这说明代码中的输出语句有误。 但用户提供的示例输入中,输出是2,而代码的输出是ans-1=1。这显然矛盾。因此,我必须新审视代码的输出部分。例如,可能代码中的ans在示例1中的值是否是2,输出ans-1=1,但用户提供的示例输出是2。这显然不可能。因此,我的分析肯定有错误。 此时,我意识到自己可能错误地理解了代码中的循环逻辑。例如,在do-while循环中,即使top=0,也会执行一次循环体。例如,第一次循环处理节点,ans增加到1。第二次循环处理节点,ans增加到2。第三次循环,top=0,ans增加到3,然后循环条件检查发现top为0,退出。输出ans-1=2,这与示例的正确输出一致。 哦,这可能是一个关键点。因为do-while循环在条件判断之前执行循环体。例如,假设在处理完所有节点后,进入循环体: - top=0,ans++,然后检查top是否为0,退出。 这样,ans会多增加一次,导致输出ans-1是正确的。 比如,在示例1中,处理两次循环后,ans=2。此时,进入第三次循环: top=0(因为没有未处理的节点),进入循环体: top=0,找不到任何节点。ans++,ans变为3。然后检查top==0,循环条件不满足,退出。输出3-1=2,与正确输出一致。 哦,原来如此!我之前没有考虑到,即使top=0,do-while循环也会执行一次循环体。这是关键错误。 例如,假设在示例1中,处理完两次循环后,所有节点都被处理。此时,进入第三次循环: top=0,ans增加到3。此时,循环条件检查top是否为0,退出循环。输出ans-1=2,正确。 这说明我的之前的分析错误,没有考虑到do-while循环的特性。即,do-while循环至少执行一次循环体,然后在每次执行后检查条件。例如,假设所有节点在两次循环后被处理,第三次循环的处理如下: do { 处理代码,top=0. ans++ } while(top !=0 ) 因此,在第一次处理时,ans=1;第二次,ans=2;第三次,处理循环体:top=0,ans=3,然后退出。输出3-1=2,正确。 这样,代码中的ans在最后一次循环中即使没有处理任何节点,ans仍然被增加。因此,正确的输出是ans-1。 现在,新模拟示例1的处理过程: 第一次循环: 处理节点2、4、7、8、9。ans=1。 第二次循环: 处理节点1、3、5、6。ans=2. 第三次循环: top=0。进入循环体,ans增加到3。然后检查top==0,循环结束。输出3-1=2。正确。 因此,代码是正确的。 这说明我之前对循环次数的分析错误,没有考虑到do-while的特性。代码中的循环会在处理完所有节点后,再执行一次循环,导致ans多增加一次。因此,最终的ans-1才是正确的层数。 例如,在示例1中,正确的层数是2层,而ans的值是3,ans-1=2。这正好正确。 现在,分析代码的正确性。每次处理一层节点,ans递增。最后一次循环没有处理任何节点,但ans仍然递增。因此,ans的值等于层数+1?或者层数等于ans-1? 例如,示例1中有两层,ans=3,输出ans-1=2。这说明层数等于ans-1。 因此,代码中的逻辑是正确的。在处理完所有层之后,ans比实际的层数多1,所以输出ans-1。 总结,该代码通过拓扑排序计算最长路径的长度,即车站分级的层数。利用do-while循环的特性,确保在每次处理完所有层之后,ans的值为层数+1,因此输出ans-1得到正确结果。</think>该代码是解决NOIP2013普及组车站分级问题的拓扑排序实现。以下是逐步解析: ### 题目分析 - **关键条件**:若车次停靠站x,则运行区间内所有≥x等级的站必须停靠。 - **目标**:确定火车站的最小分级数量。 ### 算法思路 1. **构建有向图**: - 对每个车次的停靠站,运行区间内未停靠的站j必须比所有停靠站k的等级低。 - 建立有向边$j \to k$,表示k的等级高于j。 2. **拓扑排序**: - 统计每个节点的入度(被指向的次数)。 - 每次移除入度为0的节点(当前最低等级),并将其邻接节点入度减1。 - 统计移除的层数,即为最大等级数。 ### 代码解析 ```cpp #include<iostream> #include<cstdio> #include<cstring> #define ZYS 1005 using namespace std; int n, m, ans, st[ZYS], s, tuopu[ZYS][ZYS], de[ZYS], tt[ZYS], top; bool is[ZYS], bo[ZYS]; // is标记停靠站,bo标记已处理节点 int main() { scanf("%d %d", &n, &m); for(int i=1; i<=m; i++) { memset(is, 0, sizeof(is)); scanf("%d", &s); for(int j=1; j<=s; j++) { scanf("%d", &st[j]); is[st[j]] = true; // 标记停靠站 } for(int j=st[1]; j<=st[s]; j++) { // 遍历运行区间 if(!is[j]) { // j是未停靠站 for(int k=1; k<=s; k++) { // 所有停靠站k的等级必须更高 if(!tuopu[j][st[k]]) { // 防止复建边 tuopu[j][st[k]] = 1; de[st[k]]++; // k入度增加 } } } } } do { top = 0; // 找当前入度为0且未处理的节点 for(int i=1; i<=n; i++) { if(de[i] == 0 && !bo[i]) { tt[++top] = i; bo[i] = true; } } // 移除这些节点并减少邻接节点入度 for(int i=1; i<=top; i++) { for(int j=1; j<=n; j++) { if(tuopu[tt[i]][j]) { tuopu[tt[i]][j] = 0; de[j]--; } } } ans++; // 层数+1 } while(top); // 注意do-while至少执行一次 printf("%d", ans - 1); // 最后一次循环无节点需减1 return 0; } ``` ### 关键点说明 1. **建图逻辑**: - 对每个未停靠站j,向所有停靠站k建立边$j \to k$,确保k的入度增加。 2. **拓扑排序细节**: - 使用`do-while`确保处理完所有层后仍执行一次循环,ans会多计一层,最终输出`ans-1`。 - 例如,处理2层时ans=3(循环执行3次),输出2。 3. **复杂度优化**: - 使用二维数组`tuopu`记录边,避免复处理。 - `de`数组维护入度,`bo`数组标记已处理节点。 ### 示例验证 - **输入1**:处理两次循环后ans=3,输出3-1=2,符合正确结果。 - **输入2**:同理,层数计算正确。 该算法时间复杂度为$O(m \cdot s \cdot n + n^2)$,适用于题目给定的数据规模。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值