[洛谷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;//记住输出这个
}

Bingo

posted @   Velix  阅读(31)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示