[图论最短路]D1T2

3 篇文章 0 订阅
1 篇文章 0 订阅

题目描述

月亮神树的构造非常神奇,它的枝杈交错纵横,树上甚至存在环路,可以视为一个无向带权连通图的结构。月亮神树有一个核心节点,记为 s 。要想消灭月亮神树,必须找到月亮神树的严格最不科学生成树,这是它的弱点,这样就能将其一举摧毁。
定义:一个图 G 的不科学生成树是 G 的一棵子树,在这棵子树上,从核心节点 s 到任意一个节点 u 的最短路径长度,和在原图上是等长的。其中节点 s 是月亮神树的核心节点。
定义:一个图 G 的最不科学生成树是 G 的所有不科学生成树中,边权和最小的一棵树。
定义:一个图 G 的严格最不科学生成树是 G 的所有最不科学生成树中,有序边序列的字典序最小的一棵树。
定义:一个图 G 的有序边序列是指,将图上所有边按编号从小到大排序后得到的编号序列。当然,子图子树也适用。
现在给定月亮神树,即给定一个 n 个点 m 条边的无向带权连通图,点的编号从1到n,边的编号从1到m,给定核心节点的编号 s ,求其严格最不科学生成树。

输入格式

第一行三个正整数 n,m,s 。
接下来 m 行,第 i 行三个正整数 u,v,w ,表示编号为 i 的边。
输入保证图是连通的,保证图上不含重边和自环。。

输出格式

第一行两个正整数 cnt 和 sum ,用空格隔开,其中 cnt 表示严格最不科学生成树的边的个数, sum 表示严格最不科学生成树的边权和。
接下来一行 cnt 个正整数,用空格隔开,为树上所有边的编号,按编号从小到大输出。

【样例1】
moontree.in
3 3 3
1 2 1
2 3 1
1 3 2
moontree.out
2 2
1 2
【样例2】
moontree.in
4 4 4
2 3 1
1 2 1
3 4 1
4 1 2
moontree.out
3 4
1 3 4
【数据规模与约定】
对于25%的数据, 1≤n,m≤10 。
另有25%的数据, 1≤n,m≤100 。
对于100%的数据, 1≤n,m≤3*105 ,1≤wi≤109

题解

考试中最水的一道,然而我就想骗分,想得贼复杂。
题目中写到生成树中核心结点到其他点是对应原图中的最短路距离,肯定跟最短路径有些关系。每个点肯定是通过一些最短路上的边加进生成树当中的。并且只会选择一条(不算核心结点)
首先要生成树边权和最小,则我们取一些权值较小的边替换权值较大的边
而后要选的边从小到大排的字典序最小,在权值相同的情况下取编号小的即可。
整个过程通过dij更新时选择即可。
对,就这。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<queue>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<ctime>
#define fi first
#define se second
#define N 300005
using namespace std;
struct node{
	int v,ind;
	long long w;
	node(){};
	node (int V,long long W,int I){
		v = V,w = W,ind = I;
	}
	bool operator < (const node &rhs)const {
		return w > rhs.w;
	}
};
int n,m,s;
long long sum,dis[N];
bool vis[N];
vector<node>G[N];
pair<int,int>ans[N];
bool cmp(pair<int,int> a,pair<int,int> b){
	return a.fi < b.fi;
}
void dij(){
	for (int i = 1;i <= n;i ++)
		dis[i] = (1ll << 60);
	priority_queue<node>Q;
	Q.push(node(s,0,0));
	dis[s] = 0;
	while (!Q.empty()){
		int u = Q.top().v;
		Q.pop();
		if (vis[u])
			continue;
		vis[u] = 1;
		for (int i = 0;i < G[u].size();i ++){
			int v = G[u][i].v,ind = G[u][i].ind;
			long long w = G[u][i].w;
			if (dis[v] > dis[u] + w){
				dis[v] = dis[u] + w;
				ans[v] = make_pair(ind,w);
				Q.push(node(v,dis[v],0));
			}
			if (dis[v] == dis[u] + w){
				if (w < ans[v].se)
					ans[v] = make_pair(ind,w);
				if (w == ans[v].se && ans[v].fi > ind)
					ans[v] = make_pair(ind,w);
			}
		}
	}
}
int main(){
	scanf ("%d%d%d",&n,&m,&s);
	for (int i = 1;i <= m;i ++){
		int u,v,w;
		scanf ("%d%d%d",&u,&v,&w);
		G[u].push_back(node(v,w,i));
		G[v].push_back(node(u,w,i));
	}
	dij();
	sort (ans + 1,ans + 1 + n,cmp);
	for (int i = 2;i <= n;i ++)
		sum += ans[i].se;
	printf("%d %lld\n",n - 1,sum);
	for (int i = 2;i < n;i ++)
		printf("%d ",ans[i].fi);
	printf("%d\n",ans[n].fi);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用\[1\]提供了使用Python的networkx库绘制网络图和计算最短加权路径的示例代码。该代码使用了一个包含顶点和边的列表,并使用add_nodes_from和add_weighted_edges_from方法将它们添加到图中。然后,使用nx.shortest_path_length方法计算了从顶点v1到顶点v11的最短加权路径长度为13。\[1\] 引用\[2\]提供了一个计算最短路径的Python程序示例。该程序使用了numpy和networkx库。首先,定义了一个包含顶点和边的列表,并使用add_nodes_from和add_weighted_edges_from方法将它们添加到图中。然后,使用nx.shortest_path_length方法计算了最短路径长度,并将结果存储在一个字典中。接下来,使用numpy创建了一个6x6的零矩阵,并使用两个嵌套的for循环将最短路径长度填充到矩阵中。最后,使用矩阵乘法计算了运力,并找到了最小运力和对应的位置。\[2\] 引用\[3\]提供了关于Dijkstra算法的一些背景信息。Dijkstra算法是一种寻找最短路径的算法,适用于所有权重大于等于0的情况。它可以用于解决从一个起始点到任意一个点的最短路径问题。\[3\] 综上所述,如果你想在Python中计算图论中的最短路径,可以使用networkx库和Dijkstra算法。你可以根据引用\[1\]和引用\[2\]中的示例代码进行操作。 #### 引用[.reference_title] - *1* *3* [运筹学——图论与最短距离(Python实现)](https://blog.csdn.net/weixin_46039719/article/details/122521276)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [数学建模:图论模型 — 最短路模型示例 (Python 求解)](https://blog.csdn.net/qq_55851911/article/details/124776487)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值