题目大意:求两点间最短路与长度为最短路长度+1的路径的条数之和。

方法1:最短路径+DP

首先求出ST间最短路径,然后根据递归式记忆化搜索(因此还要构造反向图)。

我们知道到达终点的路径长度最长为maxDist(T)=minDist(T)+1,而与终点相连的节点的路径长度最长为maxDist(T)-Weight(e)。这些节点与更前面的节点也是如此。于是我们从终点开始递归,利用PathCnt(v)=sum(PathCnt(u)) (PathCnt(u)=|{Dist(u)|Dist(u)<=maxDist(u)}|)。

初值注意不能直接PathCnt(S)设为1,因为S点也可能在一个环内。因此我们应当建点S'->S,边权为0。

代码:

#include <cstdio>
#include <cstring>
#include <queue>
using namespace std; #define LOOP(i,n) for(int i=1; i<=n; i++)
const int MAX_NODE = 1010, MAX_EDGE = 10010 * 2, INF = 0x3f3f3f3f; struct Node;
struct Edge; struct Node
{
int Id, Dist, PathCnt[2];
bool Inq;
Edge *Head, *RevHead;
}_nodes[MAX_NODE], *Start, *Target;
int _vCount; struct Edge
{
int Weight;
Node *From, *To;
Edge *Next;
}*_edges[MAX_EDGE];
int _eCount; void Init(int vCount)
{
_vCount = vCount;
_eCount = 0;
memset(_nodes, 0, sizeof(_nodes));
} void SetST(int sId, int tId)
{
Start = sId + _nodes;
Target = tId + _nodes;
} Edge *NewEdge()
{
_eCount++;
return _edges[_eCount] ? _edges[_eCount] : _edges[_eCount] = new Edge();
} void AddEdge(Node *from, Node *to, int weight, bool IsRev)
{
Edge *e = NewEdge();
e->From = from;
e->To = to;
e->Weight = weight;
Edge *&head = IsRev ? from->RevHead : from->Head;
e->Next = head;
head = e;
} void Build(int uId, int vId, int weight)
{
Node *from = uId + _nodes, *to = vId + _nodes;
from->Id = uId;
to->Id = vId;
AddEdge(from, to, weight, false);
AddEdge(to, from, weight, true);
} void SPFA()
{
static queue<Node*> q;
LOOP(i, _vCount)
{
_nodes[i].Dist = INF;
_nodes[i].Inq = false;
}
Start->Dist = 0;
Start->Inq = true;
q.push(Start);
while (!q.empty())
{
Node *u = q.front();
q.pop();
u->Inq = false;
for (Edge *e = u->Head; e; e = e->Next)
{
if (u->Dist + e->Weight < e->To->Dist)
{
e->To->Dist = u->Dist + e->Weight;
if (!e->To->Inq)
{
e->To->Inq = true;
q.push(e->To);
}
}
}
}
} int CntPath(Node *cur, int maxDist)
{
if (cur->Dist > maxDist)
return 0;
int &pathCnt = cur->PathCnt[maxDist - cur->Dist];
if (pathCnt != -1)
return pathCnt;
if (cur == Start)
return 1;
pathCnt = 0;
for (Edge *e = cur->RevHead; e; e = e->Next)
pathCnt += CntPath(e->To, maxDist - e->Weight);
return pathCnt;
} int main()
{
#ifdef _DEBUG
freopen("c:\\noi\\source\\input.txt", "r", stdin);
#endif
int testCase, totNode, totEdge, uId, vId, weight, sId, tId;
scanf("%d", &testCase);
while (testCase--)
{
scanf("%d%d", &totNode, &totEdge);
Init(totNode + 1);
LOOP(i, totEdge)
{
scanf("%d%d%d", &uId, &vId, &weight);
Build(uId, vId, weight);
}
scanf("%d%d", &sId, &tId);
Build(totNode + 1, sId, 0);
SetST(totNode + 1, tId);
SPFA();
LOOP(i, _vCount)
_nodes[i].PathCnt[0] = _nodes[i].PathCnt[1] = -1;
printf("%d\n", CntPath(Target, Target->Dist + 1));
}
return 0;
}

方法2:Dijkstra

同时更新一个节点的最短路径长度和次短路长度以及它们的个数。优先队列里维护节点,key值为节点最短路长度或次短路长度。究竟是哪个长度我们不用管它,该节点能更新最短路就更新最短路,否则看看能不能更新次短路。

注意:1.如果v的newDist与v原来的路径长度相等,更新完路径个数后不要入队!队列只维护最短路径或次短路径。2.更新完了v最短路,v次短路也更新了,所以要把v节点以key值为最短路长度和次短路长度入队两次。3.优先队列是大根堆。

代码:

#include <cstdio>
#include <cstring>
#include <cassert>
#include <queue>
using namespace std; #define LOOP(i,n) for(int i=1; i<=n; i++)
#define _1st 0
#define _2nd 1
const int MAX_NODE = 1010, MAX_EDGE = 10010 * 2, INF = 0x3f3f3f3f; struct Node;
struct Edge; struct Node
{
int Id, Dist[2], Cnt[2];
bool Done[2];
Edge *Head;
}_nodes[MAX_NODE], *Start, *Target;
int _vCount; struct Edge
{
int Weight;
Node *From, *To;
Edge *Next;
}*_edges[MAX_EDGE];
int _eCount; struct HeapNode
{
Node *Org;
int Dist;
bool Is2nd;
bool operator <(const HeapNode a)const { return Dist > a.Dist; }
HeapNode(Node *a, bool isSec):Org(a),Dist(a->Dist[isSec]),Is2nd(isSec){}
}; void Init(int vCount)
{
memset(_nodes, 0, sizeof(_nodes));
_vCount = vCount;
_eCount = 0;
} void SetST(int sId, int tId)
{
Start = sId + _nodes;
Target = tId + _nodes;
} Edge *NewEdge()
{
_eCount++;
if (_edges[_eCount])
return _edges[_eCount];
else
return _edges[_eCount] = new Edge();
} Edge *AddEdge(Node *from, Node *to, int weight)
{
Edge *e = NewEdge();
e->From = from;
e->To = to;
e->Weight = weight;
e->Next = e->From->Head;
e->From->Head = e;
return e;
} void Build(int uId, int vId, int weight)
{
Node *u = uId + _nodes, *v = vId + _nodes;
u->Id = uId;
v->Id = vId;
AddEdge(u, v, weight);
} void Dijkstra()
{
static priority_queue<HeapNode> q;
LOOP(i, _vCount)
{
_nodes[i].Dist[0] = _nodes[i].Dist[1] = INF;
_nodes[i].Done[0] = _nodes[i].Done[1] = false;
}
Start->Dist[_1st] = 0;
Start->Cnt[_1st] = 1;
q.push(HeapNode(Start, false));
while (!q.empty())
{
HeapNode cur = q.top();
q.pop();
Node *u = cur.Org;
if (u->Done[cur.Is2nd])
continue;
u->Done[cur.Is2nd] = true;
for (Edge *e = u->Head; e; e = e->Next)
{
int newDist = cur.Dist + e->Weight;
if (newDist < e->To->Dist[_1st])
{
e->To->Cnt[_2nd] = e->To->Cnt[_1st];
e->To->Dist[_2nd] = e->To->Dist[_1st];
e->To->Cnt[_1st] = u->Cnt[cur.Is2nd];
e->To->Dist[_1st] = newDist;
q.push(HeapNode(e->To, _1st));
q.push(HeapNode(e->To, _2nd));
}
else if (newDist > e->To->Dist[_1st] && newDist < e->To->Dist[_2nd])
{
e->To->Cnt[_2nd] = u->Cnt[cur.Is2nd];
e->To->Dist[_2nd] = newDist;
q.push(HeapNode(e->To, _2nd));
}
else if (newDist == e->To->Dist[_1st])
e->To->Cnt[_1st] += u->Cnt[cur.Is2nd];
else if (newDist == e->To->Dist[_2nd])
e->To->Cnt[_2nd] += u->Cnt[cur.Is2nd];
}
}
} int main()
{
#ifdef _DEBUG
freopen("c:\\noi\\source\\input.txt", "r", stdin);
freopen("c:\\noi\\source\\output.txt", "w", stdout);
#endif
int testCase, totNode, totEdge, uId, vId, weight, sId, tId;
scanf("%d", &testCase);
while (testCase--)
{
scanf("%d%d", &totNode, &totEdge);
Init(totNode);
LOOP(i, totEdge)
{
scanf("%d%d%d", &uId, &vId, &weight);
Build(uId, vId, weight);
}
scanf("%d%d", &sId, &tId);
SetST(sId, tId);
Dijkstra();
printf("%d\n", Target->Cnt[_1st] + ((Target->Dist[_2nd] == Target->Dist[_1st] + 1) ? Target->Cnt[_2nd] : 0));
}
return 0;
}

  错误做法:SPFA同时更新最短路和次短路。

为什么Dijkstra就可以?因为u出队时,由于Dijkstra的贪心,u本身就更新完了,无后顾之忧;而SPFA中,u还没更新完就要往下更新。u往下更新以后,如果以后运算过程中u自己再被更新,此时再由u往下更新v,v节点就错了。

POJ3463 Sightseeing的更多相关文章

  1. POJ---3463 Sightseeing 记录最短路和次短路的条数

    Sightseeing Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 9247   Accepted: 3242 Descr ...

  2. [POJ3463] Sightseeing(次短路 Heap + Dijkstra)

    传送门 用dijkstra比较好,spfa可能有的重复 dis[x][2]:dis[x][0]表示起点到x的最短路.dis[x][1]表示起点到x的次短路: tot[x][2]:tot[x][0]表示 ...

  3. poj3463 Sightseeing——次短路计数

    题目:http://poj.org/problem?id=3463 次短路计数问题,在更新最短路的同时分类成比最短路短.长于最短路而短于次短路.比次短路长三种情况讨论一下,更新次短路: 然而其实不必被 ...

  4. POJ 1637 Sightseeing tour

    Sightseeing tour Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 9276   Accepted: 3924 ...

  5. 【POJ3621】Sightseeing Cows

    Sightseeing Cows Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 8331   Accepted: 2791 ...

  6. poj1637 Sightseeing tour

    Sightseeing tour Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 8859   Accepted: 3728 ...

  7. POJ 1637 Sightseeing tour (混合图欧拉路判定)

    Sightseeing tour Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 6986   Accepted: 2901 ...

  8. POJ 1637 Sightseeing tour (混合图欧拉回路)

    Sightseeing tour   Description The city executive board in Lund wants to construct a sightseeing tou ...

  9. HDU 1688 Sightseeing&HDU 3191 How Many Paths Are There(Dijkstra变形求次短路条数)

    Sightseeing Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Tota ...

随机推荐

  1. Halcon学习笔记之支持向量机(一)

    例程:class_overlap_svm.hdev 说明:这个例程展示了如何用一个支持向量机来给一幅二维的图像进行分类.使用二维数据的原因是因为它可以很容易地联想成为区域和图像.本例程中使用了三个互相 ...

  2. HBase、Hive、MapReduce、Hadoop、Spark 开发环境搭建后的一些步骤(export导出jar包方式 或 Ant 方式)

    步骤一 若是,不会HBase开发环境搭建的博文们,见我下面的这篇博客. HBase 开发环境搭建(Eclipse\MyEclipse + Maven) 步骤一里的,需要补充的.如下: 在项目名,右键, ...

  3. 微信小程序左右滑动切换页面示例代码--转载

    微信小程序——左右滑动切换页面事件 微信小程序的左右滑动触屏事件,主要有三个事件:touchstart,touchmove,touchend. 这三个事件最重要的属性是pageX和pageY,表示X, ...

  4. webapi时间字段返回格式设置及返回model首字母小写

    GlobalConfiguration.Configuration.Formatters.Remove(new XmlMediaTypeFormatter()); // 解决json序列化时的循环引用 ...

  5. 【java基础】(2)Java父类与子类的 内存引用讲解

    从对象的内存角度来理解试试.假设现在有一个父类Father,它里面的变量需要占用1M内存.有一个它的子类Son,它里面的变量需要占用0.5M内存.现在通过代码来看看内存的分配情况:Father f = ...

  6. Eclipse的菜单简介

    在Eclipse工作台的上方提供了菜单栏,该菜单栏包含了实现Eclipse各项功能的命令,并且与编辑器相关,即菜单栏中的菜单项与当前编辑器内打开的文件是关联的.例如,编辑器内没有打开任何文件,那么,将 ...

  7. DB2数据常用指令

    ************************************************************** 这个只是个人平时总结,如果有更好的欢迎告诉我,一起学习一起成长 ***** ...

  8. 乐乎共享wifi充电宝项目

  9. 请不要继续使用VC6.0了!

    很多次和身边的同学交流,帮助同学修改代码,互相分享经验,却发现同学们依然在使用老旧的VC6.0作为编程学习的软件,不由得喊出:“请不要继续使用VC6.0了!”. VC6.0作为当年最好的IDE(集成开 ...

  10. 小功能__tab实录

    作为一个没有js基础的人来说,写一个小功能确实麻烦,也很累,从一个demo中发现details标签完美的实现菜单折叠功能,而不用费劲写好多li.div.js.发现html也是好厉害的.看来以后回家要多 ...