D. Ela and the Wiring Wizard codeforces 1737D

Problem - D - Codeforces

题目大意:有一个n个点m条边的图,(可能有重边),现可以选择任意一条边,使其与一个点断开连接,并连接该点的一个相邻点(可以自己对自己连边),费用为边权,这样的操作可以进行无数次,操作完后再跑一边最短路,问最短路+移边花费最小是多少。

思路:首先我们可以发现只有移1一条边直连1和n的时候才是最短的,因为移边的时候我们是将边的一个点移到他的相邻点上的,那么移一条边到一个点的花费就等于将这条边离目标点近的那个点开始,走最短路到目标点的路径长度*边权,那么如果我们把一条边移到了原先的最短路上,如果这条边边权比最短路上其他边权小,那么肯定要把这条边移到1,n之间,也就是替换原先的最短路,最终花费最小,如果这条边比最短路上的某一条边边权大,那么肯定移动那个边连接1,n最终花费最小。在下图样例中,最优策略是将1,2之间的边连到1,3,最后的最短路是9,

再看第三个样例,最优策略是将2,5移到2,6移到2,7移到7,7移到7,1移到1,4移到1,8,再走一遍等于7*22

综上,我们的得出的答案就是枚举每一条边i,j,将它连接1,n,有两种方案,一种儒如样例三的花费就是找到一个拐点k,找到k离i,j最近的距离再加一次k连k自己的花费,再加上k连1,k连n的花费。还有一种是如样例1,直接找1离i,j的最近距离和n离i,j的最近距离,因为点数很少,所以求距离和答案都可以用floyd实现

#include<iostream>
#include<cstdio>
#include<map>
using namespace std;
const int M = 250005, N = 505;
typedef long long ll;
const ll INF = 1e18;
ll ma[N][N];
ll dis[N][N];
int n, m;
void init()
{
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= n; j++)
		{
			if (i != j)
				ma[i][j] = dis[i][j] = INF;//初始化边权和边长为最大值
			else
				ma[i][j] = dis[i][j] = 0;//初始化点到点的边长为0
		}
	}
}
int main()
{
	int t;
	cin >> t;
	while (t--)
	{
		scanf_s("%d%d", &n, &m);
		init();
		for (int i = 1; i <= m; i++)
		{
			ll u, v, w;
			scanf_s("%lld%lld%lld", &u, &v, &w);	
			ma[u][v] = ma[v][u] = min(w, ma[u][v]);//储存点的边权,处理重边
			dis[u][v] = dis[v][u] = 1;//储存点的距离
		}
		for (int k = 1; k <= n; k++)
		{
			for (int i = 1; i <= n; i++)
			{
				for (int j = 1; j <= n; j++)
				{
					dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
					//floyd求出每两个点之间的距离
				}
			}
		}
		ll ans = INF;
		for (int k = 1; k <= n; k++)
		{
			for (int i = 1; i <= n; i++)
			{
				if (i != k && ma[i][k] < INF)
				{
					ans = min(ans, ma[k][i] * (dis[1][k] + dis[i][n] + 1));//枚举每一条边,求出将这边移到1和n之间后的花费+走一遍的花费
					for (int j = 1; j <= n; j++)
					{
						ans = min(ans, ma[k][i] * (1 + min(dis[k][j], dis[j][i]) + 1 + dis[1][j] + dis[j][n]));
						//枚举每一个点,求出以该点作为中转点进行移边+走一遍的花费
					}
				}
			}
		}
		printf("%lld\n", ans);
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

timidcatt

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值