转载请注明出处 [ametake版权所有]http://blog.csdn.net/ametake
先放上题目,出自USACO
题目描述 Description
农夫John发现做出全威斯康辛州最甜的黄油的方法:糖。把糖放在一片牧场上,他知道N(1<=N<=500)只奶牛会过来舔它,这样就能做出能卖好价钱的超甜黄油。当然,他将付出额外的费用在奶牛上。
农夫John很狡猾。他知道他可以训练这些奶牛,让它们在听到铃声时去一个特定的牧场。他打算将糖放在那里然后下午发出铃声,以至他可以在晚上挤奶。
农夫John知道每只奶牛都在各自喜欢的牧场呆着(一个牧场不一定只有一头牛)。给出各头牛在的牧场和牧场间的路线,找出使所有牛到达的路程和最短的牧场(他将把糖放在那)。
输入描述 Input Description
第一行: 三个数:奶牛数N,牧场数P(2<=P<=800),牧场间道路数C(1<=C<=1450).
第二行到第N+1行: 1到N头奶牛所在的牧场号.
第N+2行到第N+C+1行: 每行有三个数:相连的牧场A、B,两牧场间距(1<=D<=255),当然,连接是双向的.
输出描述 Output Description
一行 输出奶牛必须行走的最小的距离和.
样例输入 Sample Input
3 4 5 2 3 4 1 2 1 1 3 5 2 3 7 2 4 33 4 5
样例图形
P2 P1 @[email protected] C1 \ | \ | 5 7 3 \ | \| \ C3 C2 @[email protected] P3 P4
样例输出 Sample Output
8
{说明: 放在4号牧场最优. }
这道题目得耗了我二十多个小时 从最早的SPFA裸,到SLF和LLL优化,再到双结构体的可怜堆优化Dijkstra= =说起来都是泪啊TUT
根据朱全民在NOI导刊上的文章来看,此题最好的方法应该是SPFA
但是显然codevs加强了数据 边数剧增 导致SPFA退化到比裸的Dijkstra还慢 因为SPFA时间复杂度为O(km),m是边数,k期望为2,边数很大(比如这道题目n*(n-1),n是点数)的情况下,比dij的O(n2)显然要慢。
正如我们之前总结过的,遇到稠密图,SPFA很是苦手。
加了两个优化,原理是把可以松弛的边放在队列前面,优先队列实现。但还是超时。如下
可以看出,随着数据规模增大,时间剧增,因为边数二次函数增长
而如果用优化的Dij,效果就要好得多。堆优化的原理是运用一个最小堆(c++可以通过STL的priority_queue实现),使“查找dist最小边贪心”的复杂度降低到logn,而如果运用邻接表存储,直接查找能更新的点,更新dist的复杂度最大是n。因此堆优化后复杂度为O(nlogn),显然在稠密图中会大大降低时间复杂度。而相对于SPFA空间复杂度并没有增多很多(尤其是SPFA加了优化存储规模基本是一样的,也是写结构体+优先队列重载)。
我们来看一下dijkstra优化时间:
显而易见,小数据差别并不大,dijkstra还要慢一些,而一旦数据规模爆表,dij优势不言而喻。大数据要快10倍。
接下来放上这几次尝试的代码吧
这是最原始的SPFA 裸的可以当模板
SPFA还可以用SLF和LLL优化 两个优化本质是一样的,可以用结构体+重载小于号+优先队列实现 参见“玛丽卡”
接下来是原始版的dijkstra堆优化 和最终版区别是 元素一次性全部入队 每次最小元素出队并调整其他元素位置 这个程序WA了 但本身算法应该没有问题 我不想调试了所以先放到这里
请注意:这是错误的程序
接下来是最终版 像bfs那样 每次扩展出新的节点入队
另外,方便pascal同学们,最后附上朱全民老师pascal代码对这个题目的讲解。但是请注意,朱全民老师讲解用的是USACO原始数据,是稀疏图,最优解SPFA
——一叶扁舟轻帆卷,暂泊楚江南岸。