题目链接:Invitation Cards

分析:这是我写的第一道SPFA,没想到第一题,尽然以6000多ms的速度爬过,汗!而且加了邻接表优化,这一道题,教会我两个知识点,狂赞:好题!这题大意是:CCS有自愿者每天去各个站点宣传,让别人去看戏剧,要求这P个自愿者一个来回所需的最小花费。先求一次最短路,算出CCS到每个站点的最短距离,加起来,再将所有的边都反向一次,再计算一次CCS到每个站点的最短距离,这就相当于原图中每个站点到CCS的距离,再加起来,就是一个来回,到达每个站点的最短距离之和了。这题一点是:数据构成稀疏矩阵,用邻接表存储,比较省空间;第二点:量比较大,需用较快的算法实现,我用了SPFA,这样我就用了两次,跑了6000多ms,YM !这题还有一小陷阱:中间或结果可能出现超出32bits的整数。

 

代码
#include<stdio.h>
#include
<stdlib.h>
#include
<string.h>
#define NN 1000004
#define INF 0x7fffffff

struct node{
int sta; //边的始点
int end; //边的终点
int key; //边的权值
}edge[NN];

struct node1{
int key; //邻接的点
int next; //下一个点的下标
}pre[NN];

int n, p, q;
int que[NN], root[NN];
__int64 dis[NN];
char mark[NN];

void Add(int a, int b, int c, int id){
int tmp;
edge[id].sta
= a;
edge[id].end
= b;
edge[id].key
= c;
pre[id].key
= b;
pre[id].next
= -1;
/*由于点很多,边相对较少,所以用邻接表来存*/
if (root[a] == -1){
root[a]
= id;
}
else{
tmp
= root[a];
while (pre[tmp].next != -1){
tmp
= pre[tmp].next;
}
pre[tmp].next
= id;
}
}

void Spfa(){
int i, num, tmp, cur;
mark[
1] = 1;
que[
0] = 1;
num
= 1;

// dis赋初值
for (i = 1; i <= p; i++){
dis[i]
= INF;
}

tmp
= root[1];
dis[
1] = 0;

// 手动模拟队列
for (i = 0; i < num; i++){
cur
= que[i];
tmp
= root[cur];
while (tmp != -1){
if (dis[cur] + edge[tmp].key < dis[pre[tmp].key]){
if (!mark[pre[tmp].key]){
            mark[pre[tmp].key] = 1;
que[num
++] = pre[tmp].key;
}
dis[pre[tmp].key]
= dis[cur] + edge[tmp].key;
}
tmp
= pre[tmp].next;
}
mark[cur]
= 0;
}
}
int main()
{
int i, a, b, c;
__int64 sum;
scanf(
"%d", &n);
while (n--){
scanf(
"%d%d", &p, &q);

memset(root,
-1, sizeof(root));
for (i = 1; i <= q; i++){
scanf(
"%d%d%d", &a, &b, &c);
Add(a, b, c, i);
}
memset(mark,
0, sizeof(mark));
Spfa();
sum
= 0;
for (i = 2; i <= p; i++){
sum
+= dis[i];
}

/*将所有的边反向一次,再求得的起点到其余各点的值即为原图中各点回到起点的值*/
memset(root,
-1, sizeof(root));
for (i = 1; i <= q; i++){
a
= edge[i].end;
b
= edge[i].sta;
c
= edge[i].key;
Add(a, b, c, i);
}
memset(mark,
0, sizeof(mark));
Spfa();
for (i = 2; i <= p; i++){
sum
+= dis[i];
}
printf(
"%I64d\n", sum);
}
return 0;
}

 

邻接表:邻接表是,将所有节点n的后继结点用一个链表的形式存起来,以节点n做首节点。

SPFA:以队列的形式优化Bellman_Ford算法,时间复杂度优化到O(ke),其中k为所有顶点进队的平均次数,可以证明k一般小于等于2,e为边数。

百度百科SPFA

posted on 2010-07-13 21:34  ylfdrib  阅读(509)  评论(0编辑  收藏  举报