Luogu P1685 游览/JZOJ #333 神秘岛 (图论-拓扑排序)

本文介绍了如何解决Luogu P1685和JZOJ #333的神秘岛问题,该问题涉及到图论和拓扑排序。通过将桃花岛抽象为无环有向图,求解所有不同路线的总时间。解题策略包括求拓扑序并进行递推计算,详细解题思路和代码实现一同呈现。

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

来源:Luogu P1685,JZOJ #333

题目描述

顺利通过了黄药师的考验,下面就可以尽情游览桃花岛了!

你要从桃花岛的西头开始一直玩到东头,然后在东头的码头离开。可是当你游玩了一次后,发现桃花岛的景色实在是非常的美丽!!!于是你还想乘船从桃花岛东头的码头回到西头,再玩一遍,但是桃花岛有个规矩:你可以游览无数遍,但是每次游玩的路线不能完全一样。

我们把桃花岛抽象成了一个图,共 nnn 个点代表路的相交处,mmm 条边表示路,边是有向的(只能按照边的方向行走),且可能有连接相同两点的边。输入保证这个图没有环,而且从西头到东头至少存在一条路线。两条路线被认为是不同的当且仅当它们所经过的路不完全相同。

你的任务是:把所有不同的路线游览完一共要花多少时间?

解题思路

  • 这是一道较为综合的题,囊括拓扑排序和递推,是一道经典的好题;
  • 首先,求一个拓扑序,存在队列 qqq 中,然后在暴力枚举递推一遍,Total[i]Total[i]Total[i] 表示走到第 iii 个点的方案数,Times[i]Time_s[i]Times[i] 表示走到第 iii 个点花费的时间;
  • 可得两个递推公式
 	Total[y]=(Total[x]+Total[y]) % Mod;
	Time_s[y]=(Time_s[x]+Time_s[y]+Total[x]*e[j].v) % Mod;

代码君

#include <bits/stdc++.h>
using namespace std;
const int Mod=10000;
int tot=0,n,m,st,en,Time;
int linkk[50010],q[50010],Total[50010],Time_s[50010],in[50010];
struct node
{
	int y,v,next;
}e[50010];
inline int read()
{
   int s=0,w=1;
   char ch=getchar();
   while(ch<'0'||ch>'9') {if(ch=='-')w=-1;ch=getchar();}
   while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
   return s*w;
}
inline void write(int x)
{
    if(x<0)
	{
		putchar('-');
		x=-x;
	}
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
void insert(int x,int y,int v)  //邻接表
{
	e[++tot].y=y; e[tot].v=v;
	e[tot].next=linkk[x]; linkk[x]=tot;
}
void Topsort()  //拓扑排序
{
	int head=1,tail=0;
	for (int i=1;i<=n;i++)
	 if (in[i]==0) q[++tail]=i;  //初始化
	for (head=1;head<=tail;head++)
	{
		int x=q[head];  //取出队头
		for (int i=linkk[x];i;i=e[i].next)  //邻接表查询
		{
			int y=e[i].y;
			if (--in[y]==0) q[++tail]=y;  //入队
		}
		if (tail==n) return;
	}
}
void Recurrence()
{
	Total[st]=1; Time_s[st]=0;
	for (int i=1;i<=n;i++)
	{
		int x=q[i];
		for (int j=linkk[x];j;j=e[j].next)
		{
			int y=e[j].y;
			Total[y]=(Total[x]+Total[y]) % Mod;  //转移方程
			Time_s[y]=(Time_s[x]+Time_s[y]+Total[x]*e[j].v) % Mod;
		}
	}
	printf("%d",(Time_s[en]+(Total[en]-1)*Time) % Mod);  //最后别忘了乘Time
}
int main()
{
	freopen("road.in","r",stdin);
	freopen("road.out","w",stdout);
	scanf("%d %d %d %d %d",&n,&m,&st,&en,&Time);
	for (int i=1;i<=m;i++)
	{
		int x,y,v;
		scanf("%d %d %d",&x,&y,&v);
		insert(x,y,v);
		in[y]++;  //入度
	}
	Topsort();
	Recurrence();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值