做了第一道差分约束系统的题后,趁热打铁再来一道。没想到这题就是纯粹的对短路径问题卡时间的题目。理解题意有需要注意的地方,我开始是SPFA(1),得到dis[n],再SPFA(n),得到dis[1],再取两者较大值。实际上只用算前者即可,dis[n]即为答案。
1. 刚开始直接写了SPFA的队列实现,不用说肯定是超时.
2. 听说栈实现SPFA会比较快,又改写成了栈,结果还是超时了,这就奇怪了,discuss都说栈能过啊。我用的是标准的图的邻接表存储,也就是链表实现的,但我看别人都用的数组来模拟的,我想是不是数组模拟的比较快?又改了,还是悲剧超时了.
3. 又发现我的代码有一点和别人不一样,我添加边是add(b,a,k),人家都是add(a,b,k),但我觉得应该没有影响啊,一个意思。我改成add(a,b,k)后就AC了(500+MS).
4. 再反过来改成我原先的标准图的邻接表存储,又超时了。这就让我有点想不通了,不是都说指针操作的效率比较高吗?怎么这就体现不出来呢,更何况这题时间上限是1.5S,用数组模拟就0.5S,用链表就超时....真想不通.
5. discuss里面说这题"点少边多,宜用Dij",普通的Dij时间复杂度是(n^2)肯定超时的,所以要用"优先队列"来实现每个循环中找最小的dis[i]值的过程,看人家都是用c++的STL中的priority_queue.悲剧的是我是c.所以得自己写堆实现优先队列了。研究了半天,写出来了。悲剧的是直接wrong了.
6. 纠结.....纠结......找bug....我找...找...终于找到一个c语言同胞.分析了一下代码确实有个难以发现的漏洞:每次找到一个最近的点,即为小根堆中的根Q[1],把改点从队列删除,此时必须跟新堆,即HeapAdjust(1),(这个过程我的程序没有问题),再松弛和该点相邻的点,松弛之后dis[]就发生了变化,必须再次更新堆(这个我掉了).....修补bug and AC (600+MS). oh...yes...
7. 这题又贡献了我30多个Submit。。。
我的SPFA(栈实现):
#include
#include
#define INF 100000000
struct Edge
{
int e, v;
} edge[150005];
int neg;
int node[30005];
int next[150005];
int n;
int dis[30005], vst[30005];
int Q[30005];
void add(int s, int e, int v)
{
edge[neg].e = e;
edge[neg].v = v;
next[neg] = node[s];
node[s] = neg++;
}
int relax(int s, int e, int v)
{
if (dis[s]+v < dis[e])
{
dis[e] = dis[s]+v;
return 1;
}
return 0;
}
/* 用栈来实现试试 */
int SPFA(int s0)
{
int i, t, p, top;
for (i=1; i<=n; i++)
dis[i] = INF;
dis[s0] = 0;
Q[0] = s0;
top = 1;
vst[s0] = 1;
while (top)
{
t = Q[--top];
p = node[t];
while (p != -1)
{
if (relax(t, edge[p].e, edge[p].v) && !vst[edge[p].e])
{
Q[top++] = edge[p].e;
vst[edge[p].e] = 1;
}
p = next[p];
}
vst[t] = 0;
}
return 1;
}
int main()
{
int m, k, a, b;
memset(node, -1, sizeof(node));
scanf("%d %d", &n, &m);
while (m--)
{
scanf("%d %d %d", &a, &b, &k);
add(a, b, k);
}
SPFA(1);
printf("%d/n", dis[n]);
}
我的Dij+heap:
#include
#include
#include
#define MAXV 30005
#define MAXE 150005
#define INF 1000000000
struct Edge
{
int e, v;
} edge[MAXE];
int n, neg;
int dis[MAXV], vst[MAXV];
int node[MAXV], next[MAXE];
int Q[MAXV];
int index[MAXV];
void add(int s, int e, int v)
{
edge[neg].e = e;
edge[neg].v = v;
next[neg] = node[s];
node[s] = neg++;
}
void swap(int x, int y)
{
int t;
t = Q[x];
Q[x] = Q[y];
Q[y] = t;
index[Q[x]] = x;
index[Q[y]] = y;
}
void heapAdjust(int root)
{
int l, r, min;
l = 2*root;
r = 2*root+1;
min = root;
if (l <= Q[0] && dis[Q[l]] < dis[Q[min]])
min = l;
if (r <= Q[0] && dis[Q[r]] < dis[Q[min]])
min = r;
if (min != root)
{
swap(root, min);
heapAdjust(min);
}
}
void buildHeap()
{
int i;
Q[0] = n;
for (i=1; i<=n; i++)
{
Q[i] = i;
index[i] = i;
}
for (i=n/2; i>=1; i--)
heapAdjust(i);
}
/* 因为dis[Q[root]]变小了,要更新堆,使Q[root]沿着树枝往上移到合适的位置 */
void update(int root)
{
while (root/2 && dis[Q[root]] < dis[Q[root/2]])
{
swap(root, root/2);
root = root/2;
}
}
int getMin()
{
int min;
min = Q[1];
Q[1] = Q[Q[0]];
Q[0]--;
index[Q[1]] = 1;
heapAdjust(1);
return min;
}
/* Dijkstra算法,优先队列实现 */
void Dijkstra(int s0)
{
int i, k, p;
for (i=1; i<=n; i++)
dis[i] = INF;
dis[s0] = 0;
buildHeap();
while (Q[0])
{
k = getMin();
vst[k] = 1;
p = node[k];
while (p != -1)
{
if (!vst[edge[p].e] && dis[k]+edge[p].v