POJ 1511 Invitation Cards【SPFA】


http://poj.org/problem?id=1511
POJ 1511 Invitation Cards
大意:求有向图中从源点到每个节点的往返距离和
分析:
   1.根据题意建立两幅图,分别对应于正向图和反向图
   2.分别对正向图和反向图求从源点即题中的1结点出发求単源最短路径,
   3. 由2易得,正向图中求得的dis[i]为s到i的最短路,反向图中求得的dis[i]为i到s的最短路
      那么s到i的往返距离即为两个dis相加
   4.在求単源最短路时用SPFA,两次SPFA即可完成

   重点注意:需要读入的数据较大,使用getint()函数可大大减少读数据时间   

View Code
1 #include < stdio.h >
2 #include < string .h >
3 #include < queue >
4 #include < iostream >
5   using namespace std;
6 const int N = 1000000 + 5 ; // 顶点个数
7 struct EDGE
8 {
9 long v;
10 long price;
11 EDGE * next;
12 }edges[ 2 ][N], * ind[ 2 ][N]; // 使用指针实现邻接表的存储
13
14 __int64 dis[N];
15 int ednum; // 记录边数
16 bool isInQueue[N]; // isInQueue[i]标记i当前是否在队列中
17 void spfa( int eid, int sid, int n) // spfa求eid图中从sid出发的単源最短路径
18 {
19 queue < int > myQueue;
20 int i,j;
21 for (i = 1 ;i <= n;i ++ )
22 {
23 dis[i] =- 1 ;
24 isInQueue[i] = false ;
25 }
26
27 int cur;
28 cur = sid;
29 dis[cur] = 0 ;
30 isInQueue[cur] = true ;
31 myQueue.push(cur);
32
33 while ( ! myQueue.empty())
34 {
35 cur = myQueue.front(); // 每次仅对队列中的点进行松弛操作
36 myQueue.pop();
37 isInQueue[cur] = false ;
38 for (EDGE * p = ind[eid][cur];p;p = p -> next)
39 {
40 __int64 temp = p -> price + dis[cur];
41 if (dis[p -> v] ==- 1 || temp < dis[p -> v])
42 {
43 dis[p -> v] = temp;
44 if (isInQueue[p -> v] == false )
45 {
46 myQueue.push(p -> v);
47 isInQueue[p -> v] = true ;
48 }
49 }
50 }
51
52 }
53
54 }
55
56 // 添加新边
57 inline void addEdge( long a, long b, long c)
58 {
59 EDGE * po, * op;
60 po = & edges[ 0 ][ednum]; // 正向边
61 op = & edges[ 1 ][ednum]; // 反向边
62 ednum ++ ;
63
64 po -> price = c;
65 po -> v = b;
66 po -> next = ind[ 0 ][a];
67 ind[ 0 ][a] = po;
68
69 op -> price = c;
70 op -> v = a;
71 op -> next = ind[ 1 ][b];
72 ind[ 1 ][b] = op;
73
74 }
75
76 inline long getint() // 这个getchar的输入对大数据量输入非常有用,甚至可以挽救效率不高的算法
77 {
78 long ret = 0 ;
79 char tmp;
80 while ( ! isdigit(tmp = getchar()));
81 do {
82 ret = (ret << 3 ) + (ret << 1 ) + tmp - ' 0 ' ;
83 } while (isdigit(tmp = getchar()));
84 return ret;
85 }
86
87 int main()
88 {
89 int T;
90 while (scanf( " %d " , & T) != EOF)
91 {
92 while (T -- )
93 {
94 int P,Q;
95 scanf( " %d%d " , & P, & Q); // P节点个数,Q边个数
96 for ( int i = 0 ;i <= P;i ++ )
97 {
98 ind[ 0 ][i] = NULL;
99 ind[ 1 ][i] = NULL;
100 }
101
102 ednum = 0 ;
103 long a,b,c;
104 while (Q -- )
105 {
106 // scanf("%ld%ld%ld",&a,&b,&c);
107 a = getint();
108 b = getint();
109 c = getint();
110 addEdge(a,b,c);
111 }
112 __int64 ans = 0 ;
113 for ( int i = 0 ;i < 2 ;i ++ ) // 分别对应于求正向图与反向图的単源最短路
114 {
115 spfa(i, 1 ,P);
116 for ( int j = 1 ;j <= P;j ++ )
117 ans += dis[j];
118 }
119
120 printf( " %I64d\n " ,ans);
121 }
122 }
123 return 0 ;
124 }

转载于:https://www.cnblogs.com/AndreMouche/archive/2011/03/31/2001440.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值