hdu 1636 Invitation Cards //spfa 详解;

In the age of television, not many people attend theater performances. Antique Comedians of Malidinesia are aware of this fact. They want to propagate theater and, most of all, Antique Comedies. They have printed invitation cards with all the necessary information and with the programme. A lot of students were hired to distribute these invitations among the people. Each student volunteer has assigned exactly one bus stop and he or she stays there the whole day and gives invitation to people travelling by bus. A special course was taken where students learned how to influence people and what is the difference between influencing and robbery.
The transport system is very special: all lines are unidirectional and connect exactly two stops. Buses leave the originating stop with passangers each half an hour. After reaching the destination stop they return empty to the originating stop, where they wait until the next full half an hour, e.g. X:00 or X:30, where 'X' denotes the hour. The fee for transport between two stops is given by special tables and is payable on the spot. The lines are planned in such a way, that each round trip (i.e. a journey starting and finishing at the same stop) passes through a Central Checkpoint Stop (CCS) where each passenger has to pass a thorough check including body scan.
All the ACM student members leave the CCS each morning. Each volunteer is to move to one predetermined stop to invite passengers. There are as many volunteers as stops. At the end of the day, all students travel back to CCS. You are to write a computer program that helps ACM to minimize the amount of money to pay every day for the transport of their employees.

Input

The input consists of N cases. The first line of the input contains only positive integer N. Then follow the cases. Each case begins with a line containing exactly two integers P and Q, 1 <= P,Q <= 1000000. P is the number of stops including CCS and Q the number of bus lines. Then there are Q lines, each describing one bus line. Each of the lines contains exactly three numbers - the originating stop, the destination stop and the price. The CCS is designated by number 1. Prices are positive integers the sum of which is smaller than 1000000000. You can also assume it is always possible to get from any stop to any other stop.

Output

For each case, print one line containing the minimum amount of money to be paid each day by ACM for the travel costs of its volunteers.

Sample Input

2
2 2
1 2 13
2 1 33
4 6
1 2 10
2 1 60
1 3 20
3 4 10
2 4 5
4 1 50

Sample Output

46
210
spfa 详解
spfa原理和bfs基本一样,看以下思路

们用数组d记录每个结点的最短路径估计值,用邻接表来存储图G。我们采取的方法是动态逼近法:设立一个先进先出的队列用来保存待优化的结点,优化时每次取出队首结点u,并且用u点当前的最短路径估计值对离开u点所指向的结点v进行松弛操作,如果v点的最短路径估计值有所调整,且v点不在当前的队列中,就将v点放入队尾。这样不断从队列中取出结点来进行松弛操作,直至队列空为止

 

期望的时间复杂度O(ke), 其中k为所有顶点进队的平均次数,可以证明k一般小于等于2。

 

实现方法:

  建立一个队列,初始时队列里只有起始点,再建立一个表格记录起始点到所有点的最短路径(该表格的初始值要赋为极大值,该点到他本身的路径赋为0)。然后执行松弛操作,用队列里有的点作为起始点去刷新到所有点的最短路,如果刷新成功且被刷新点不在队列中则把该点加入到队列最后。重复执行直到队列为空。

判断有无负环:   如果某个点进入队列的次数超过N次则存在负环(SPFA无法处理带负环的图)

 

 

 

 

首先建立起始点a到其余各点的 最短路径表格

                                  

首先源点a入队,当队列非空时:  1、队首元素(a)出队,对以a为起始点的所有边的终点依次进行松弛操作(此处有b,c,d三个点),此时路径表格状态为:

                                  

在松弛时三个点的最短路径估值变小了,而这些点队列中都没有出现,这些点 需要入队,此时,队列中新入队了三个结点b,c,d

队首元素b点出队,对以b为起始点的所有边的终点依次进行松弛操作(此处只有e点),此时路径表格状态为:

                                 

在最短路径表中,e的最短路径估值也变小了,e在队列中不存在,因此e也要 入队,此时队列中的元素为c,d,e

队首元素c点出队,对以c为起始点的所有边的终点依次进行松弛操作(此处有e,f两个点),此时路径表格状态为:

                                 

在最短路径表中,e,f的最短路径估值变小了,e在队列中存在,f不存在。因此 e不用入队了,f要入队,此时队列中的元素为d,e,f

 队首元素d点出队,对以d为起始点的所有边的终点依次进行松弛操作(此处只有g这个点),此时路径表格状态为:

 

 

                               

在最短路径表中,g的最短路径估值没有变小(松弛不成功),没有新结点入队,队列中元素为f,g

队首元素f点出队,对以f为起始点的所有边的终点依次进行松弛操作(此处有d,e,g三个点),此时路径表格状态为:

                               

在最短路径表中,e,g的最短路径估值又变小,队列中无e点,e入队,队列中存在g这个点,g不用入队,此时队列中元素为g,e

队首元素g点出队,对以g为起始点的所有边的终点依次进行松弛操作(此处只有b点),此时路径表格状态为:

                           

在最短路径表中,b的最短路径估值又变小,队列中无b点,b入队,此时队列中元素为e,b 队首元素e点出队,对以e为起始点的所有边的终点依次进行松弛操作(此处只有g这个点),此时路径表格状态为:

 

                          

在最短路径表中,g的最短路径估值没变化(松弛不成功),此时队列中元素为b

队首元素b点出队,对以b为起始点的所有边的终点依次进行松弛操作(此处只有e这个点),此时路径表格状态为:

                         

在最短路径表中,e的最短路径估值没变化(松弛不成功),此时队列为空了

最终a到g的最短路径为14

但是这是思路而已,怎样实现这个操作真的是个骚操作,关于实现原理这个还真没在网上找到过,我这个算是第一篇吧;
这个题意就是找到从起始点到每个点的最小距离和加上从终点到每个点的最小距离;
哎,要好好学英语,也是在网上找的题意,这个题真的不好理解意思
 
 
FlagPIDTitleSourceACAllVID
Yes1510Invitation CardsCentral Europe 199836HDU1535
Yes6870Invitation CardsCentral Europe 199838PKU1511
Yes10389Invitation CardsSummer Training I--Graph922FZU1636
Yes16917Invitation CardsCentral Europe 199833ZJU2008
网上有的代码因为某些oj数据谁能过,像,fzu,或者,pku,可能就过不了;
详细看我代码注释
 
 
#include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<algorithm> #include<stack> #include<queue> using namespace std; #define inf 0xffffffff #define MAX 1000010 struct {  int e, w, next; }edge[2][MAX]; int n, m; int headlist[2][MAX], vis[MAX]; long long d[MAX], ans; void spfa(int cab) {  int i, v, b;//v 和b 都是来记录状态  for (i = 0; i <= n; i++)//初始化  {   d[i] = inf;   vis[i] = 0;  }  queue<int>q;//开队列 先进先出  q.push(1);  vis[1] = 1;  d[1] = 0;  while (!q.empty())  {   v = q.front();   q.pop();   vis[v] = 0;   for (i = headlist[cab][v]; i != -1; i = edge[cab][i].next)//一次进队,找到每个与起始点相连的点离起始点的最短距离//类似于并查集的思路   {    b = edge[cab][i].e;    if (d[b] > edge[cab][i].w+d[v])    {     d[b] = edge[cab][i].w + d[v];     if (!vis[b])//一次进队后把二层子节点看作起始点入队,重复操作,也就是把b标记下推进队列     {      vis[b] = 1;      q.push(b);     }    }   }
 } } int main() {  int t;int i, j;  scanf("%d", &t);  {   while (t--)   {    int a, b, w;        scanf("%d%d", &n, &m);    for (i = 0; i <= n; i++)    {     headlist[0][i] = -1;     headlist[1][i] = -1;    }
//这个使用临间表储存的;headlist相当于指针把他们穿起来    for (i = 0; i < m; i++)    {     scanf("%d%d%d", &a, &b, &w);     edge[0][i].w = w;//价值     edge[0][i].e = b;//子节点     edge[0][i].next = headlist[0][a];//其实这一行代码和下一行代码不好理解,真的,
//自己写几个案例模拟一下
//比如
//1 2 1
//2 3 1
//3 4 1
//4 5 1
//第一组
//1 2 1
//1 3 1
//1 4 1
//第二组
//想我比较low写了好多数据才理解;     headlist[0][a] = i;//记录是第几位数据;     edge[1][i].w = w;     edge[1][i].e = a;     edge[1][i].next = headlist[1][b];     headlist[1][b] = i;    }    ans = 0;    spfa(0);    for (i = 1; i <= n; i++) ans += d[i];    spfa(1);    for (i = 1; i <= n; i++) ans += d[i];        cout << ans << endl;//printf("%lld\n", ans);他喵的每个oj的FAQ都好神奇,奇奇怪怪的标准,fzu数据大,要开8个f才行,还有输出 longlong型,只能用cout 不然会翻车   }  }  return 0; }
最后,想简单的朋友在我看来这是个,标准到极致的模板,可以直接使用,也不必深入理解,像我
深入理解他的每个私处,打了不下10遍,才搞定原理,10遍,是交了能ac的,想起来花了4天每天都在问学长,和重复理解她的意思

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值