第七周 floyd,dijuskal,图中负环,路径解析

7.1.floyd算法解决传递闭包问题
问题:在游戏中,两人之间会存在胜负关系,寻找不能确定胜负关系的二元组合。
前提:(a,b) (b,a)属于同一个二元组合。

问题分析:一个人可以视为一个点,胜负关系可理解为两个点之间存在边。
则问题转化为在图中寻找中转可连通的传递闭包。

实现:用矩阵对点关系进行存储,两点有胜负关系则记为1。
注意:对于一条边而言,我们在矩阵中只更新一个值为1。

floyd算法说明:在图中不断加入可中转点,加入中转点后对图的连通到达情况进行更新。
算法空间复杂度为:n^2 算法的时间复杂度为:n^3

本题中的剪枝说明:
在本题中,对于一个点的自身而言,(a,a)始终为:0。
因而在中转点等于 行数、列数; 或行数 = 列数 时,都应进行剪枝。
if(!map[j][k]) continue; 当第一个中转点不可达时,中转无意义,进行剪枝。

最终结果:图矩阵中非0个数,代表确定胜负关系的二元组。
二元组的个数为:n*(n-1)
故不确定的二元组为:总二元组数目-确定胜负关系的二元组

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
 
const int maxn = 500+5;
int n,m,map[maxn][maxn];
 
void floyd()
{
   
	for(int k=1;k<=n;k++)  
			for(int j=1;j<=n;j++)
			{
   
				if(j==k) continue;
				if(!map[j][k]) continue;
				for (int p = 1; p <= n; p++)
				{
   
					if(j==p||p==k) continue;
					map[j][p]=map[j][p]|(map[j][k]&map[k][p]);
				}
		   }
 
 
}
 
int main()
{
   
	int u,v;
	int num;
	scanf("%d", &num);
	for (int i = 0; i < num; i++)
	{
   
		scanf("%d%d", &n, &m);
		memset(map,0,sizeof(map));
		for(int i = 1; i <= m; i++)
		{
   
			scanf("%d%d",&u,&v);
			map[u][v] = 1;
		}
		
		floyd();
		 
		int count=0;
		for(int i=1; i<=n;i++)
		 for(int j=1; j<=n;j++)
		 {
   
		 	if(map[i][j]!=0)
		 	count++;		 	
		 }
		 
		 int ans=n*(n-1)/2;
		 ans=ans-count;
		
		printf("%d\n",ans);
	}
	return 0;
}

7.2.dijuskal算法寻找最短路径问题

题目描述:
众所周知,TT 有一只魔法猫。

今天他在 B 站上开启了一次旅行直播,记录他与魔法猫在喵星旅游时的奇遇。
TT 从家里出发,准备乘坐猫猫快线前往喵星机场。
猫猫快线分为经济线和商业线两种,它们的速度与价钱都不同。
当然啦,商业线要比经济线贵,TT 平常只能坐经济线,
但是今天 TT 的魔法猫变出了一张商业线车票,可以坐一站商业线。
假设 TT 换乘的时间忽略不计,请你帮 TT 找到一条去喵星机场最快的线路,
不然就要误机了!

input:
输入包含多组数据。每组数据第一行为 3 个整数 N, S 和 E
(2 ≤ N ≤ 500, 1 ≤ S, E ≤ 100),即猫猫快线中的车站总数,
起点和终点(即喵星机场所在站)编号。

下一行包含一个整数 M (1 ≤ M ≤ 1000),即经济线的路段条数。
接下来有 M 行,每行 3 个整数 X, Y, Z (1 ≤ X, Y ≤ N, 1 ≤ Z ≤ 100),
表示 TT 可以乘坐经济线在车站 X 和车站 Y 之间往返,其中单程需要 Z 分钟。

下一行为商业线的路段条数 K (1 ≤ K ≤ 1000)。
接下来 K 行是商业线路段的描述,格式同经济线。
所有路段都是双向的,但有可能必须使用商业车票才能到达机场。保证最优解唯一。

output:
对于每组数据,输出3行。第一行按访问顺序给出 TT 经过的各个车站(包括起点和终点)
第二行是 TT 换乘商业线的车站编号
(如果没有使用商业线车票,输出"Ticket Not Used",不含引号)
第三行是 TT 前往喵星机场花费的总时间。

本题不忽略多余的空格和制表符,且每一组答案间要输出一个换行

dijuskal算法的思想是:
选择权重最小的边,然后对所有的边权进行更新。
这样,在一个图中,我们进行(n-1)次选择。
也就是说到每个点的路径都是当前可选的最短路径。
因而,通过该算法选出了最小连通路径。

实现:本题采用链式前向星结构对点与边进行处理,
这样可将当前起点所连的所有边进行对应存储,便于之后遍历。

首先对经济线利用dijuskal算法:
找出最小连通路径,并在确定最小连通路径的过程中,
用dis[]、pre[]数组对当前起点到对应点的距离进行标记,
对到达点的前一个点进行标记。
通过对起点 s,终点 e,利用两次dijuskal算法。将数据进行统计。

对于一次商务线的选择:
当不选择商务线时,最短路径值就是dis1[e],从s出发到e的距离。
是否选择商务线,在于加入该条商务线,能否缩短当前的最小路径。
遍历所有商务线,选取使得路径最短的那一条。

关于路径的输出:
1.不选择商务线进行路径输出
pre1[]数组中存放当前点的前一个节点。
首先拿出节点e 再不断找出上一个节点,直到找到节点s 停止。
这样将路径统计到数组a[]中,进行处理输出即可。

2.选择商务线进行路径输出
选择了商务线,那么该商务线的两端记为 ans_u,ans_v;
s—>ans_u 使用s起始的连通路径;
ans_v—>e 使用e起始的连通路径;
因而通过 pre1[]从ans_u起始,向前遍历找到s,并对寻找的过程点进行标记。
对pre2[]从e开始,向前遍历找到e,并对寻找过程进行标记。

#include<iostream>
#include<cstring>
using namespace std;

const int maxx=1000000;
int n,m,k,s,e,num; 
int ans=maxx,head[maxx],dis1[maxx],dis2[maxx];
int pre1[maxx],pre2[maxx],sum,a[maxx];
bool flag[maxx];
int ans_u,ans_v,ans_w;

struct node
{
   
	int to;
	int w;
	int next;
}e1[maxx];

void add_edge(int from,int to,int w)
{
   
	e1[++num].to=to;
	e1[num].w=w;
	e1[num].next=head[from];
	head[from
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值