POJ3463 Sightseeing
题目大意:求两点间最短路与长度为最短路长度+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的更多相关文章
- POJ---3463 Sightseeing 记录最短路和次短路的条数
Sightseeing Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 9247 Accepted: 3242 Descr ...
- [POJ3463] Sightseeing(次短路 Heap + Dijkstra)
传送门 用dijkstra比较好,spfa可能有的重复 dis[x][2]:dis[x][0]表示起点到x的最短路.dis[x][1]表示起点到x的次短路: tot[x][2]:tot[x][0]表示 ...
- poj3463 Sightseeing——次短路计数
题目:http://poj.org/problem?id=3463 次短路计数问题,在更新最短路的同时分类成比最短路短.长于最短路而短于次短路.比次短路长三种情况讨论一下,更新次短路: 然而其实不必被 ...
- POJ 1637 Sightseeing tour
Sightseeing tour Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 9276 Accepted: 3924 ...
- 【POJ3621】Sightseeing Cows
Sightseeing Cows Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 8331 Accepted: 2791 ...
- poj1637 Sightseeing tour
Sightseeing tour Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 8859 Accepted: 3728 ...
- POJ 1637 Sightseeing tour (混合图欧拉路判定)
Sightseeing tour Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 6986 Accepted: 2901 ...
- POJ 1637 Sightseeing tour (混合图欧拉回路)
Sightseeing tour Description The city executive board in Lund wants to construct a sightseeing tou ...
- 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 ...
随机推荐
- python--6、logging模块
logging 可用的日志级别: debug 10 info 20 warning 30 error 40 critical 50 logging默认参数: 默认日志级别是warning. 默认情况日 ...
- android黑科技系列——爆破一款应用的签名验证问题
一.前言 在之前的文章中说过Android中的安全和破解是相辅相成的,为了防止被破解,很多应用做了一些防护策略,但是防护策略也是分等级,一般简单的策略就是混淆代码和签名校验,而对于签名校验很多应用都是 ...
- js-var变量作用域
看代码: var a=10; function fn1(){ alert(a); var a=20; alert(a); } 运行结果:undefined 和 20 注意: 在函数内,变量如没用var ...
- 时序分析:KMP算法用于序列识别
考研基础资料之一的<算法与数据结构>,KMP算法作为串匹配的基本算法,为必考题目之一.对于算法入门来说,也是复杂度稍高的一个基本算法. KMP算法作为串匹配的非暴力算法,是为了减少回溯而设 ...
- EF MySql:Specified key was too long; max key length is 767 bytes解决方案
[DbConfigurationType(typeof(MySqlEFConfiguration))]//添加特性 public partial class Model1 : DbContext { ...
- servlet_获取初始化参数
在web.xml的<servlet>标签中增添 <init-param> <param-name>XXX</param-name> <param- ...
- jq 跨域请求
//使用getJSON <script type="text/javascript"> $(function () { $("#btn2").cli ...
- webstorm前端开发工具vue环境配置及运行项目
1:webstorm的安装:2:node.js的安装3:安装Git4:vue-cli 安装前面两步就可以把项目启动了,安装Git主要是打开命令窗口,这样就可以用liunx命令了,原理跟cmd差不多 V ...
- inherit 关键字使得元素获取其父元素的计算值
它可以应用于任何CSS属性,包括CSS简写 all. 对于继承属性,inherit 关键字只是增强了属性的默认行为,只有在重载(overload)其它规则的时候被使用.对于非继承属性,inherit ...
- Day9 面向对象进阶
@property装饰器 之前我们讨论过Python中属性和方法访问权限的问题,虽然我们不建议将属性设置为私有的,但是如果直接将属性暴露给外界也是有问题的,比如我们没有办法检查赋给属性的值是否有效.我 ...