【次小生成树】4.秘密的牛奶运输

题目描述

Farmer John 要把他的牛奶运输到各个销售点。运输过程中,可以先把牛奶运输到一些销售点,再由这些销售点分别运输到其他销售点。

运输的总距离越小,运输的成本也就越低。
Farmer John 期望低成本的运输,但他并不想让他的竞争对手知道他具体的运输方案,
所以他希望采用费用第二小的运输方案而不是最小的。现在请你帮忙找到该运输方案。

输入格式
第一行是两个整数 N,M,表示顶点数和边数;

接下来 M 行每行 3 个整数,x,y,z,表示一条路的两端 x,y 和距离 z。

输出格式
仅一行,输出第二小方案。

样例
输入数据 1

4 4
1 2 100
2 4 200
2 3 250
3 4 100

输出数据 1

450

1  ---100---  2
                  /  \ 
             250   200
                /      \
            3 -100 - 4

数据范围与提示
对于全部数据,1<=N<=500 , 1<=M <=10^4 ,1<=z<=10^9 ,
数据可能有重边。

分析:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=510,M=1e4+10;
int n,m;
int p[N];
int dist1[N][N],dist2[N][N];//最大距离和次大距离 
int head[N],e[N*2],w[N*2],ne[N*2],index;

struct Arcnode //边的类型 
{
	int x,y,z;
	bool flag=false;
	bool operator<(const edge &e)const
	{
		return z<e.z;
	}
}arcs[M];

int find(int x)//找 p[x]==x 
{
	if(p[x]!=x)
		p[x]=find(p[x]);//递归 
	return p[x];
}

void add(int x,int y,int z)
{
	e[index]=y; //存终点 
	w[index]=z; //存权值 
	ne[index]=head[x];//存 head[起点] 
	head[x]=index; //更新head[起点] = 第几个点 
	index++;
} 

//深度优先遍历 
//起点u 终点fa 判断是否回到原起点 maxd1最大值 maxd2次大值
//d1[u] 存储从now->u的最大边权值 d2[u]保存从now->的次大边权值 
//循环遍历与u连接的每一个点v,如果没有回到终点fa就继续 
void dfs(int start,int end,int maxd1,int maxd2,int d1[],int d2)
{
	d1[start]=maxd1;
	d2[start]=maxd2;
	for(int i=head[start] ; ~i ;i=ne[i])
	{
		int j=e[i];//存弧头 终点 
		if(j!=end)
		{
			int td1=maxd1;
			int td2=maxd2;
			if(w[i]>td1)//i权值 大于 最大距离 
				td2=td1,td1=w[i];//次大距离=最大距离,最大距离=i权值 
			else if(w[i]>td2 &&w[i]<td1)//
				td2=w[i];
			dfs(j,start,td1,td2,d1,d2);//递归 往前 
		}
	}
}

int main()
{
	scanf("%d%d",&n,&m)//n是顶点数,m是边数 
	memset(head,-1,sizeof(head));

	//输入边 
	for(int i=0;i<m;i++){
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		arcs[i]={x,y,z};
	}
	sort(arcs,arcs+m);
//	sort(arcs,arcs+m,cmp);如果没有重载< 
	
	//对于求次小生成树来说,两种算法的思路都是相同的。
	/*首先求出最小生成树,
	再枚举每条不在最小生成树上的边,
	并把这条边放到最小生成树上面,
	此时一定会形成环,
	那么在这条环路中除去一条除新加入的边外的最长边,
	最终得到的权值就是次小生成树的权值。*/
	for(int i=0;i<m;i++)
	{
		p[i]=i;
	}
	ll sum=0;
	for(int i=0;i<m;i++)//遍历每一条边 
	{
		int x=arcs[i].x;
		int y=arcs[i].y;
		int z=arcs[i].z;
		int px=find(x); //p[x]==x
		int py=find(y);
		if(px!=py)//如果x y不相等 
		{
			p[px]=py;//更新 赋值 
			sum+=z;//累加权值 
			//将这条边 标记 
			add(x,y,z);
			add(y,x,z);
			arcs[i].flag=true;
		}
	}
	for(int i=1;i<=n;i++)//深度遍历 
	{
		//起点,终点,最大值,次大值, 
		dfs(i,-1,-1,-1,dist1[i],dis2[i]);
	}
	ll res=1e18;
	for(int i=0;i<m;i++)
	{
		if(!arcs[i].flag )//如果是假,没有加入最小生成树 
		{
			int x=arcs[i].x;
			int y=arcs[i].y;
			int z=arcs[i].z;
			ll temp;
			if(z>dist1[x][y])//这条边的权值> x->y的最大距离 
				temp=sum+w-dist1[a][b];
			else if(z>dist2[x][y])
				temp=sum+w-dist2[a][b];
			res=min(res,temp);//最小值 
		} 
	}
	printf("%lld\n",res);

	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值