POJ 3463 Sightseeing (次短路,Dijkstra拓展)
2014-01-25 21:35
295 查看
据说用 A* 求 k 短路,令 k = 2 ,会爆栈。
做这道题目主要还是学习为主。。
显然求最短路和次短路的数量,再判断二者是不是差 1 即可。
由于要求次短路,对 Dijkstra 进行修改。
dis[] 数组不再只存最短路,而是用二维数组同时存最短路和次短路,初始化只有起点 s 的最短路是 0,其他均为 inf
用 cnt[][2] 来计数,第一维伟最短路数量,第二维为次短路数量,初始化起点最短路为 1,其他为 0。
存在队列里的”状态“应该包括:1、结点标号 num 2、标签 flag:最短路还是次短路(后面解释为什么这样)
每次弹出 dis[num][flag] 最小的状态,表示 num 的 flag 路径 已经找到,其正确性同理于原始的 Dijkstra。
对结点 num 的邻接点进行松弛。
每次的松弛分为:1、比邻接点的最短路还短
2、等于最短路
3、大于最短路小于次短路
4、等于次短路
分别进行相应操作即可。
这里和 A* 不一样,存在队列里的”状态“,不应该包含相应的计数的值,因为 Dijkstra 保证每个点的每种路径只出队一次,故当时的 dis 和 num 就是我们要用的(出队的一定是该路径的最优解,而 dis 最终也一定是最优解覆盖的,cnt 同理)
用结点 cur 松弛结点 tar 时,应该把对应的 cnt 传过去,因为如果从 A···B 有 n 条路,则用路径 A···B 松弛得到的的 A···B→C 松弛后也应该标记为 n 条或 cnt + n 条(根据具体情况)。
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int maxv = 1100, maxe = 11000, inf = 0x3f3f3f3f;
int n, m, s, t;
int head[maxv], next[maxe];
int fr[maxe], to[maxe], wt[maxe];
void read()
{
memset(head, -1, sizeof(head));
scanf("%d%d", &n, &m);
for(int i = 0; i < m; i++) {
scanf("%d%d%d", &fr[i], &to[i], &wt[i]);
fr[i]--; to[i]--;
next[i] = head[fr[i]];
head[fr[i]] = i;
}
scanf("%d%d", &s, &t);
s--; t--;
}
int dis[maxv][2];
int cnt[maxv][2];
bool done[maxv][2];
typedef struct Node {
int num, tag;
Node(int n, int t) {
num = n;
tag = t;
}
bool operator<(Node b)const{
return dis[num][tag] > dis[b.num][b.tag];
}
}Node;
priority_queue<Node> que;
int dijkstra()
{
memset(done, false, sizeof(done));
memset(dis, 0x3f, sizeof(dis));
memset(cnt, 0, sizeof(cnt));
dis[s][0] = 0;
cnt[s][0] = 1;
que.push(Node(s, 0));
while(!que.empty()) {
Node cn = que.top(); que.pop();
int cur = cn.num, cf = cn.tag;
if(done[cur][cf]) continue;
done[cur][cf] = true;
for(int i = head[cur]; i != -1; i = next[i]) {
int tar = to[i];
if(dis[cur][cf] + wt[i] < dis[tar][0]) {
dis[tar][1] = dis[tar][0];
cnt[tar][1] = cnt[tar][0];
if(dis[tar][1] < inf) que.push(Node(tar, 1));
dis[tar][0] = dis[cur][cf] + wt[i];
cnt[tar][0] = cnt[cur][cf];
que.push(Node(tar, 0));
}
else if(dis[cur][cf] + wt[i] == dis[tar][0])
cnt[tar][0] += cnt[cur][cf];
else if(dis[cur][cf] + wt[i] < dis[tar][1]) {
dis[tar][1] = dis[cur][cf] + wt[i];
cnt[tar][1] = cnt[cur][cf];
que.push(Node(tar, 1));
}
else if(dis[cur][cf] + wt[i] == dis[tar][1])
cnt[tar][1] += cnt[cur][cf];
}
}
int ret = cnt[t][0];
if(dis[t][1] - dis[t][0] == 1) ret += cnt[t][1];
return ret;
}
int main()
{
int ca;
scanf("%d", &ca);
while(ca--) {
read();
printf("%d\n", dijkstra());
}
return 0;
}
做这道题目主要还是学习为主。。
显然求最短路和次短路的数量,再判断二者是不是差 1 即可。
由于要求次短路,对 Dijkstra 进行修改。
dis[] 数组不再只存最短路,而是用二维数组同时存最短路和次短路,初始化只有起点 s 的最短路是 0,其他均为 inf
用 cnt[][2] 来计数,第一维伟最短路数量,第二维为次短路数量,初始化起点最短路为 1,其他为 0。
存在队列里的”状态“应该包括:1、结点标号 num 2、标签 flag:最短路还是次短路(后面解释为什么这样)
每次弹出 dis[num][flag] 最小的状态,表示 num 的 flag 路径 已经找到,其正确性同理于原始的 Dijkstra。
对结点 num 的邻接点进行松弛。
每次的松弛分为:1、比邻接点的最短路还短
2、等于最短路
3、大于最短路小于次短路
4、等于次短路
分别进行相应操作即可。
这里和 A* 不一样,存在队列里的”状态“,不应该包含相应的计数的值,因为 Dijkstra 保证每个点的每种路径只出队一次,故当时的 dis 和 num 就是我们要用的(出队的一定是该路径的最优解,而 dis 最终也一定是最优解覆盖的,cnt 同理)
用结点 cur 松弛结点 tar 时,应该把对应的 cnt 传过去,因为如果从 A···B 有 n 条路,则用路径 A···B 松弛得到的的 A···B→C 松弛后也应该标记为 n 条或 cnt + n 条(根据具体情况)。
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int maxv = 1100, maxe = 11000, inf = 0x3f3f3f3f;
int n, m, s, t;
int head[maxv], next[maxe];
int fr[maxe], to[maxe], wt[maxe];
void read()
{
memset(head, -1, sizeof(head));
scanf("%d%d", &n, &m);
for(int i = 0; i < m; i++) {
scanf("%d%d%d", &fr[i], &to[i], &wt[i]);
fr[i]--; to[i]--;
next[i] = head[fr[i]];
head[fr[i]] = i;
}
scanf("%d%d", &s, &t);
s--; t--;
}
int dis[maxv][2];
int cnt[maxv][2];
bool done[maxv][2];
typedef struct Node {
int num, tag;
Node(int n, int t) {
num = n;
tag = t;
}
bool operator<(Node b)const{
return dis[num][tag] > dis[b.num][b.tag];
}
}Node;
priority_queue<Node> que;
int dijkstra()
{
memset(done, false, sizeof(done));
memset(dis, 0x3f, sizeof(dis));
memset(cnt, 0, sizeof(cnt));
dis[s][0] = 0;
cnt[s][0] = 1;
que.push(Node(s, 0));
while(!que.empty()) {
Node cn = que.top(); que.pop();
int cur = cn.num, cf = cn.tag;
if(done[cur][cf]) continue;
done[cur][cf] = true;
for(int i = head[cur]; i != -1; i = next[i]) {
int tar = to[i];
if(dis[cur][cf] + wt[i] < dis[tar][0]) {
dis[tar][1] = dis[tar][0];
cnt[tar][1] = cnt[tar][0];
if(dis[tar][1] < inf) que.push(Node(tar, 1));
dis[tar][0] = dis[cur][cf] + wt[i];
cnt[tar][0] = cnt[cur][cf];
que.push(Node(tar, 0));
}
else if(dis[cur][cf] + wt[i] == dis[tar][0])
cnt[tar][0] += cnt[cur][cf];
else if(dis[cur][cf] + wt[i] < dis[tar][1]) {
dis[tar][1] = dis[cur][cf] + wt[i];
cnt[tar][1] = cnt[cur][cf];
que.push(Node(tar, 1));
}
else if(dis[cur][cf] + wt[i] == dis[tar][1])
cnt[tar][1] += cnt[cur][cf];
}
}
int ret = cnt[t][0];
if(dis[t][1] - dis[t][0] == 1) ret += cnt[t][1];
return ret;
}
int main()
{
int ca;
scanf("%d", &ca);
while(ca--) {
read();
printf("%d\n", dijkstra());
}
return 0;
}
相关文章推荐
- POJ 3463 Sightseeing (Dijkstra 次短路)
- POJ---3463 Sightseeing[Dijkstra()求最短路和次短路条数][好题]
- POJ 3463 Sightseeing (Dijkstra 次短路)
- poj 3463 Sightseeing(次短路+条数统计)
- POJ-3463: Sightseeing 【最短路次短路及条数】
- poj 3463 Sightseeing(最短路+次短路)
- poj 3463 Sightseeing 最短路和次短路计数
- POJ 3463 Sightseeing(次短路)
- 3463 Sightseeing dijkstra求最短路和次短路
- poj 3463 Sightseeing 最短路和次短路的总数
- POJ 3463 Sightseeing(次短路问题)
- poj 3463(最短路和比最短路大1的路的数量)(dijkstra)
- POJ 3463 Sightseeing (第k短路)
- POJ 3463 Sightseeing (最短路&次短路条数问题)
- POJ 3463 Sightseeing(最短路次短路计数)
- POJ --- 3463 or HDU 1688 Sightseeing 【次短路 + 最短路 + Dij】
- POJ 3463 && HDU 1688 Sightseeing 次短路
- 通过POJ 3463 Sightseeing(dijkstra)彻底理解优先队列优化的dijkstra算法
- poj 3463 dijkstra变形(求最短路和次短路的数量)
- POJ 3463 Sightseeing【次短路】