Luogu P2966 [USACO09DEC]牛收费路径-Cow Toll Paths (图论-Floyd)

来源:Luogu P2966,JZOJ #318

题目描述

跟所有人一样,农夫约翰以着宁教我负天下牛,休叫天下牛负我的伟大精神,日日夜夜苦思生 财之道。为了发财,他设置了一系列的规章制度,使得任何一只奶牛在农场中的道路行走,都要向农夫约翰上交过路费。

农场中由 N 1 < = N < = 250 N(1 <= N <= 250) 片草地(标号为 1 1 N N ),并且有 M 1 < = M < = 10000 M(1 <= M <= 10000) 条双向道路连接草地 A j A_j B j 1 < = A j < = N ; 1 < = B j < = N B_j(1 <= A_j <= N; 1 <= B_j <= N) 。奶牛们从任意一片草地出发可以抵达任意一片的草地。 F J FJ 已经在连接 A j A_j B j B_j 的双向道路上设置一个过路费 L j 1 < = L j < = 100 , 000 L_j(1 <= L_j <= 100,000) 。 可能有多条道路连接相同的两片草地,但是不存在一条道路连接一片草地和这片草地本身。最值得庆幸的是,奶牛从任意一篇草地出发,经过一系列的路径,总是可以抵达其它的任意一片草地。除了贪得无厌,叫兽都不知道该说什么好。

FJ竟然在每片草地上面也设置了一个过路费 C i 1 < = C i < = 100000 C_i(1 <= C_i <= 100000) 。从一片草地到另外一片草地的费用,是经过的所有道路的过路费之和,加上经过的所有的草地(包括起点和终点)的过路费的最大值。

任劳任怨的牛们希望去调查一下她们应该选择那一条路径。她们要你写一个程序,接受 K 1 < = K < = 10 , 000 K(1 <= K <= 10,000) 个问题并且输出每个询问对应的最小花费。第i个问题包含两个数字 s i s_i t i 1 < = s i < = N ; 1 < = t i < = N ; s i ! = t i t_i(1 <= s_i <= N; 1 <= t_i <= N; s_i != t_i) ,表示起点和终点的草地。

考虑下面这个包含 5 5 片草地的样例图像:
在这里插入图片描述
从草地 1 1 到草地 2 2 的道路的“边过路费”为 3 3 ,草地 2 2 的“点过路费”为 5 5

要从草地 1 1 走到草地 4 4 ,可以从草地 1 1 走到草地 3 3 再走到草地 5 5 最后抵达草地 4 4 。如果这么走的话, 需要的“边过路费”为 2 + 1 + 1 = 4 2+1+1=4 ,需要的点过路费为 4 4 (草地 5 5 的点过路费最大),所以总的花 费为 4 + 4 = 8 4+4=8

而从草地 2 2 到草地 3 3 的最佳路径是从草地 2 2 出发,抵达草地 5 5 ,最后到达草地 3 3 。这么走的话,边过路费为 3 + 1 = 4 3+1=4 ,点过路费为 5 5 ,总花费为 4 + 5 = 9 4+5=9

解题思路

  • N 1 < = N < = 250 N(1 <= N <= 250) ,点很少,最暴力的方法就是 F l o y d Floyd 的,写起来简洁明了;
  • 首先,我们按点权从小到大排序,有点权小的不拿白不拿嘛;
  • 这里注意:有一个预处理,就是开一个结构体,记录点的编号 c [ i ] . p o s = i ; c[i].pos=i; ,再另外开一个数组 r a n k rank , r a n k [ c [ i ] . p o s ] rank[c[i].pos] 表示原来编号为 c [ i ] . p o s c[i].pos 的点排在第 r a n k [ c [ i ] . p o s ] rank[c[i].pos] 个;,以便在排序后能方便快捷地找到排序前与排序后点的对应位置;
  • 可是这个新奇的点权怎么处理呢?这着实是个问题。
  • F l o y d Floyd 中,因为 k k 的枚举是从小到大的,所以 m a x ( c [ i ] . x , m a x ( c [ k ] . x , c [ j ] . x ) ) max(c[i].x,max(c[k].x,c[j].x)) 为此路径的最大点权

代码君

#include <bits/stdc++.h>
using namespace std;
const int MAXN=250+10,MAXM=10000*2+10;
int n,m,q;
int dis[MAXN][MAXN],Cos[MAXN][MAXN],rank[MAXM];
struct node
{
	int x,pos;
}c[MAXN]={};
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');
}
bool mycmp(node a,node b)  //按点权排序
{
	return a.x<b.x;
}
int main()
{
	freopen("toll.in","r",stdin);
	freopen("toll.out","w",stdout);
	n=read(); m=read(); q=read();
	for (int i=1;i<=n;i++)
	{
		c[i].x=read();
		c[i].pos=i;  //记录点编号
	}
	sort(c+1,c+n+1,mycmp);
	for (int i=1;i<=n;i++)
	{
		rank[c[i].pos]=i;  //原来编号为c[i].pos的点排在第rank[c[i].pos]个
	}
	memset(dis,10,sizeof(dis));  //初值:正无穷
	memset(Cos,10,sizeof(Cos));
	for (int i=1;i<=m;i++)
	{
		int x=read(),y=read(),v=read();
		dis[rank[x]][rank[y]]=min(dis[rank[x]][rank[y]],v);  //邻接矩阵
		dis[rank[y]][rank[x]]=min(dis[rank[x]][rank[y]],v);
	}
	for (int i=1;i<=n;i++) dis[i][i]=0;
	for (int k=1;k<=n;k++)
	 for (int i=1;i<=n;i++)
	  for (int j=1;j<=n;j++)
	  {
	  	if (i!=j)
	  	{
	  		dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);  //最短路
	  		Cos[i][j]=min(Cos[i][j],dis[i][j]+max(c[i].x,max(c[k].x,c[j].x)));  //加上点权的最短路
		}
	  }
	for (int i=1;i<=q;i++)  //q次查询
	{
		int st,en;
		st=read(); en=read();
		write(Cos[rank[st]][rank[en]]);  //输出答案
		printf("\n");
	}
	return 0;
}
发布了27 篇原创文章 · 获赞 33 · 访问量 1689

猜你喜欢

转载自blog.csdn.net/qq_43081996/article/details/104161606