[洛谷P1983] 车站分级
上个月做完了旅行计划,本来想做这道题的,可最终拖了一个月才A掉
原题链接
理解题意
这题请尽量看懂题目,否则直接GG。
题上有一个条件被隐藏了,但它非常重要:
途中停靠点等级>途中未停靠点等级
那么解决题目的方法就出来了:
1. 制造一个图
2. 将每一次行驶途中停靠的点向未停靠点连一个边
类似于这样(0,1,2,3,4表示停靠点,5,6,7,8,9表示未停靠点)
全部弄完后直接上一次拓扑排序,求出最长路(方法请参考最后程序)。
时间复杂度约为
Θ
(
n
m
2
)
\Theta(nm^2)
Θ(nm2)
在洛谷上常数小的话能过,大了直接凉凉。
一个看似简单但能让程序跑的嗷嗷快的优化
很明显,超时的原因是因为连的边太多。
那如何减少边数呢?
这就是我们要讲的
虚点优化
不要听着觉得很难,实际上它要做的只有一件事:
将每次途中停靠点连到虚点,又将虚点连到每个未停靠点
还是画个图容易理解:
简单吧。
最后输出
(
a
n
s
+
1
)
/
2
(ans+1)/2
(ans+1)/2
为什么?
因为由于虚点的存在,答案会翻一倍。
而又由于虚点的存在,答案的节点可能会停在虚点上。
这里就给出代码:
#include<bits/stdc++.h>
using namespace std;
struct qq{
int next,to;
}a[2000000];
int head[2000000],n,m,c[2000],d,tot,v[200000],ans,g,e[200000];
//head和tot用于邻接表,n和m不解释,c和d用来输入,v指的是入度
//g用于构造虚点,e指的是深度(即最长路),ans就是答案
int que[300000],h,t;
//que是队列,广搜型拓扑排序必备,不在此做过多解释
void add(int i,int j)//邻接表
{
a[++tot].to=j;
a[tot].next=head[i];
head[i]=tot;
}
void topo()//拓扑排序
{
while(h<t)
{
h++;
for(int i=head[que[h]];i;i=a[i].next)
{
v[a[i].to]--;//入度减1
e[a[i].to]=e[que[h]]+1;//更新深度
ans=max(ans,e[a[i].to]);//更新答案
if(!v[a[i].to]) que[++t]=a[i].to;//如果入度为0就入队
}
}
}
int main()
{
cin>>n>>m;
g=n;
for(int i=1;i<=m;i++)
{
++g;//构建虚点
cin>>d>>c[1];
add(c[1],g);
v[g]++;
for(int j=2;j<=d;j++)
{
scanf("%d",&c[j]);
add(c[j],g),v[g]++;
for(int k=c[j-1]+1;k<c[j];k++)//虚点向途中未经过的点连边
add(g,k),v[k]++;
}
}
for(int i=1;i<=n;i++)//如果入度为0就入队
if(!v[i])
{
que[++t]=i;
e[i]=1;
}
topo();
cout<<(ans+1)/2;//记住输出这个
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律