香甜的黄油
题目描述
农夫John发现做出全威斯康辛州最甜的黄油的方法:糖。把糖放在一片牧场上,他知道N(1≤N≤500)只奶牛会过来舔它,这样就能做出能卖好价钱的超甜黄油。当然,他将付出额外的费用在奶牛上。
农夫John很狡猾。像以前的巴甫洛夫,他知道他可以训练这些奶牛,让它们在听到铃声时去一个特定的牧场。他打算将糖放在那里然后下午发出铃声,以至他可以在晚上挤奶。
农夫John知道每只奶牛都在各自喜欢的牧场(一个牧场不一定只有一头牛)。给出各头牛在的牧场和牧场间的路线,找出使所有牛到达的路程和最短的牧场(他将把糖放在那)。
输入样例:
3 4 5
2
3
4
1 2 1
1 3 5
2 3 7
2 4 3
3 4 5
输出: 8
思路:也就是求最短路径,即求解所有奶牛所在牧区距离放糖的牧区之间的距离之和最短。
这边不能采用传统的求解最短路径的算法。我们采用SPFA算法(Shortest Path Faster Algorithm)
SPFA算法详解
参考博客:https://blog.csdn.net/muxidreamtohit/article/details/7894298
适用范围:给定的图存在负权边,这时类似Dijkstra等算法便没有了用武之地,而Bellman-Ford算法的复杂度又过高,SPFA算法便派上用场了。 我们约定有向加权图G不存在负权回路,即最短路径一定存在。当然,我们可以在执行该算法前做一次拓扑排序,以判断是否存在负权回路,但这不是我们讨论的重点。
算法思想:我们用数组d记录每个结点的最短路径估计值,用邻接表来存储图G。我们采取的方法是动态逼近法:设立一个先进先出的队列用来保存待优化的结点,优化时每次取出队首结点u,并且用u点当前的最短路径估计值对离开u点所指向的结点v进行松弛操作,如果v点的最短路径估计值有所调整,且v点不在当前的队列中,就将v点放入队尾。这样不断从队列中取出结点来进行松弛操作,直至队列空为止
期望的时间复杂度O(ke), 其中k为所有顶点进队的平均次数,可以证明k一般小于等于2。
实现方法:
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
int G[801][801], hd[100000], tot, d[100010];
// d[i]代表i奶牛在哪个牧区
struct node{
int x, y, next, w;
}a[100010]; // 边结构
int v[100010];
int p[100010]; // 定义队列
// 邻接表
void add(int x, int y, int z){
tot++;
a[tot].x = x; // 边头结点
a[tot].y = y; // 边尾结点
a[tot].w = z; // 边的权值
a[tot].next = hd[x]; // 指向上一条边(具有相同的头结点)
hd[x] = tot; // 统计以x为头结点的边数
// cout << "a["<< tot << "].next:" << a[tot].next << endl;
}
void spfa(int x){
memset(v, 0, sizeof(v)); // 标记顶点是否在队列中
G[x][x] = 0;
queue<int> p; // 创建列表
p.push(x); // 源点进入列表
v[x] = 1;
while(!p.empty()){ // 判断列表是否为空
int x1 = p.front();
p.pop(); // 出队列
v[x1] = 0;
for(int j=hd[x1];j; j=a[j].next){
if(G[x][a[j].y]>G[x][x1]+a[j].w){
G[x][a[j].y] = G[x][x1]+a[j].w; // 松弛操作
if(v[a[j].y] == 0){
// v[x1] = 1;
p.push(a[j].y); // 刷新成功且被刷新点不在队列中则把该点加入到队列最后
v[a[j].y] = 1;
}
}
}
}
}
int main(){
memset(G, 100, sizeof(G)); //将G的空间赋值为100
//奶牛数N,牧场数P(2≤P≤800),牧场间道路数C(1≤C≤1450)。
int n, p, c;
cin >> n >> p >>c;
//奶牛的存储位置
for(int i=1; i<=n; i++){
cin >> d[i];
}
// 输入牧场间的通道
for(int i=1; i<=c; i++){
int x, y, z;
cin >> x >> y >> z;
add(x, y, z);
add(y, x, z);
}
for(int i=1; i<=p; i++){
spfa(i);
}
int min_dis = 100000000;
for(int i=1; i<=p; i++){
int ans = 0;
for(int j=1; j<=n; j++){
ans += G[d[j]][i];
}
min_dis = min(min_dis, ans);
}
cout << min_dis;
return 0;
}