次短路
目前只会用dijstra求解。。(貌似有用spfa的。。
当然如果是DAG, 拓扑排序加DP也是可以的。。
次短路的求解和计数
hdu 3191
这道题有边权为0的情况。
dij所使用的贪心性质:S是目标集合(已经求得最短路或次短路的点),从 V - S 中选估计值最小的一个u,那么
d[u]
就是u的最短路长度。
如果边权有0存在, 那么这条性质就无法保证了。。
幸而数据比较特殊, 只要保证编号小的点先更新就行了。。
// 这道题的数据里面有权值为0的情况,pq并不能保证正确
// 详见 http://acm.hdu.edu.cn/discuss/problem/list.php?problemid=3191
int n, m, src, dst;
struct Edge {
int v, w;
};
vector<Edge> graph[N];
struct Node {
int flag, d, i;
bool operator < (const Node &rhs) const {
if ( d != rhs.d ) return d > rhs.d;
return i > rhs.i;
}
};
const int INF = 1e9;
int d[2][N], cnt[2][N];
bool done[2][N];
void dij() {
memset(done, 0, sizeof(done));
memset(cnt, 0, sizeof(cnt));
fill(d[0], d[0] + n, INF);
fill(d[1], d[1] + n, INF);
d[0][src] = 0;
cnt[0][src] = 1;
d[1][src] = -1;
priority_queue<Node> q;
q.push( (Node) {0, 0, src} );
while(!q.empty()) {
Node fr = q.top(); q.pop();
int u = fr.i, du = fr.d;
if ( done[fr.flag][u] ) continue;
done[fr.flag][u] = 1;
for (int i = 0; i < graph[u].size(); ++i) {
int v = graph[u][i].v, w = du + graph[u][i].w, num = cnt[fr.flag][u];
if ( d[0][v] > w ) {
if ( d[0][v] != INF ) {
d[1][v] = d[0][v];
cnt[1][v] = cnt[0][v];
q.push( (Node) {1, d[1][v], v} );
}
d[0][v] = w;
cnt[0][v] = num;
q.push( (Node) {0, d[0][v], v} );
} else if ( !done[0][v] && d[0][v] == w ) {
cnt[0][v] += num;
} else if ( d[1][v] > w ) {
cnt[1][v] = num;
d[1][v] = w;
q.push( (Node) {1, d[1][v], v} );
} else if ( !done[1][v] && d[1][v] == w ) {
cnt[1][v] += num;
}
}
}
printf("%d %d\n", d[1][dst], cnt[1][dst]);
}
int main() {
#ifdef _LOCA_ENV_
freopen("input.in", "r", stdin);
#endif // _LOCA_ENV
while ( scanf("%d%d%d%d", &n, &m, &src, &dst) != EOF ) {
rep(i, 0, n-1) graph[i].clear();
rep(i, 1, m) {
int x, y, w;
scanf("%d%d%d", &x, &y, &w);
graph[x].push_back( (Edge) {y, w} );
}
dij();
}
return 0;
}
次短路拓展
lightoj 1099
求无向图上次短路值, 可以往回走。。
因为是无向图, 所以往回走并不影响什么。。
只要“往回走”得到的次短路比较优, 贪心性质一定会保证它被选中。
也可以不用done数组, 直接用值比较来判断这个点是否已经用来更新过了。。
int n, m, src, dst;
struct Edge {
int v, w;
};
vector<Edge> graph[N];
struct Node {
int flag, d, i;
bool operator < (const Node &rhs) const {
if ( d != rhs.d ) return d > rhs.d;
return i > rhs.i;
}
};
const int INF = 1e9;
int d[2][N];
bool done[2][N];
void dij() {
memset(done, 0, sizeof(done));
fill(d[0], d[0] + n, INF);
fill(d[1], d[1] + n, INF);
d[0][src] = 0;
d[1][src] = INF;
priority_queue<Node> q;
q.push( (Node) {0, 0, src} );
while(!q.empty()) {
Node fr = q.top(); q.pop();
int u = fr.i, du = fr.d;
if ( done[fr.flag][u] ) continue;
done[fr.flag][u] = 1;
for (int i = 0; i < graph[u].size(); ++i) {
int v = graph[u][i].v, w = du + graph[u][i].w;
if ( d[0][v] > w ) {
if ( d[0][v] != INF ) {
d[1][v] = d[0][v];
q.push( (Node) {1, d[1][v], v} );
}
d[0][v] = w;
q.push( (Node) {0, d[0][v], v} );
} else if ( d[0][v] == w ) {
// pass
} else if ( d[1][v] > w ) {
d[1][v] = w;
q.push( (Node) {1, d[1][v], v} );
}
}
}
}
Dijstra拓展
当最短路径有多条的时候, 我们可以倒过来递推出, 所有最短路径。
还可以对pq使用的HeapNode节点添加一些性质, 在不影响最短路值的情况下, 决定优先用哪些边来更新。
codeforces 449B - Jzzhu and Cities
在不影响1到各点最短路值的情况下, 尽量去掉 给定边集里面的边。