【解题思路】
将题目叙述转为图论概念,牧场为顶点,牧场间的路线为边。该题一个顶点上有多头牛,这个后面再考虑。记有V个顶点,E条边,有n头牛,每头牛所在顶点为v 1 , v 2 , . . . v n ,d(i,j)为顶点i,j间的最短路径长度。题目要问的问题是,选择一个顶点c,使得
最小。
如果已知每头牛所在顶点到其它任意顶点的最短路径长度,那么接下来就可以遍历所有顶点,记遍历到的顶点为i,选择顶点i为放糖的地方,求所有牛所在顶点到顶点i的最短路径加和,这一步的复杂度为O(V⋅n)。问题落在如何求每头牛所在顶点到其它任意顶点的最短路径。
使用Floyd算法,求所有顶点间的最短路径长度。题目给定顶点数(牧场数)最大为800,Floyd算法的复杂度为O(V^3),800^3=5.12*10^8,复杂度达到10^8数量级很可能会超时,所以不能用Floyd算法。
如果用单源最短路径算法,需要对每头牛所在顶点跑一次单源最短路径算法。算法整体复杂度为:O(n)乘以所用的单源最短路径算法的复杂度。
- 考虑使用朴素Dijkstra算法,整体复杂度为O(n⋅V^2),计算 500*800^2=3.2*10^8,不可行。
- 使用SPFA算法,复杂度为O(kE),k为每个顶点平均入队次数,在稀疏图中一般小于2,可以认为等于2。用SPFA算法做该问题,整体复杂度为O(n⋅kE),计算 500*2*1500 = 1.5*10^6,可行。
以上是思考过程,该题做法为:
遍历每个顶点v,求每个牛所在顶点到v的最短路径长度乘以那里牛的数量,再加和。求这些加和中的最小值。
【参考代码】
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=805;
int n,p,c;//奶牛数n 牧场数p 牧场间道路数c
int b[N],num[N],w[N][N];
int a[N][N],dis[N],qu[10000];
bool v[N];
int x,y,t,minnl;
int main()
{
cin>>n>>p>>c;
for(int i=1;i<=p;i++)
for(int j=1;j<=p;j++) w[i][j]=0x7f7f7f7f;
for(int i=1;i<=n;i++)cin>>b[i];//第i头奶牛所在的牧场
for(int i=1;i<=c;i++){
cin>>x>>y>>t;
w[x][y]=w[y][x]=t;//邻接矩阵
a[x][++num[x]]=y;//num[i]i牧场邻接牧场的个数
a[y][++num[y]]=x;//a[y][1~num[y]]储存所有与y牧场邻接的牧场号
}
minnl=0x7f7f7f7f;
for(int i=1;i<=p;i++){//p个牧场
for(int j=1;j<=p;j++) dis[j]=0x7f7f7f7f;
memset(qu,0,sizeof(qu));//队列初始化
memset(v,false,sizeof(v));//初始化访问
dis[i]=0;qu[1]=i;//初始化距离 i牧场入队
int head=0,tail=1;//队头、队尾
v[i]=true;//已访问标记
do{
head++;
int u=qu[head];
v[u]=false;
for(int j=1;j<=num[u];j++)
if(dis[a[u][j]]>dis[u]+w[u][a[u][j]]){
dis[a[u][j]]=dis[u]+w[u][a[u][j]];
if(!v[a[u][j]]){
tail++;
qu[tail]=a[u][j];
v[a[u][j]]=true;
}
}
}while(head<tail);
int temp=0;
for(int j=1;j<=n;j++) temp+=dis[b[j]];
if(temp<minnl)minnl=temp;
}
cout<<minnl;
return 0;
}