最短路计数
题面
思路
和DP中的计数问题类似;
首先我们得先求出最小值是多少;
再求出当前这个状态能由哪几个状态转移最小值过来;
需要注意,DP问题都是满足拓扑序的,但是图论的问题不一定;
因此我们要满足更新是符合拓扑序的;
最短路计数首先满足条件是不能存在值为0的环,因为存在的话那么被更新的点的条数就为 I N F INF INF了;
接着,我们需要把图抽象成最短路树,或者称为拓扑图;
而我们求最短路一般有三种方法;
- BFS,适用于边权为1的情况;
- 迪杰斯特拉系列(包括0-1BFS),适用于非负权图;
- SPFA,基本都适用;
因为要满足依赖是拓扑序,我们分析一下这三类方法;
BFS,每个点只会入队出队各一次,必然是拓扑序的;
迪杰斯特拉系列,每个点只会出队一次,因此也可以抽象成拓扑序;
SPFA,每个点会多次入队出队,故天生不带有拓扑序;
至于为什么,以及如果想使用SPFA怎么办呢?见下图;
因为这题是无权图,因此我们可以将看成边权只有 1 1 1,那我们直接写个BFS即可;
Code
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <utility>
#include <queue>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int n,m;
struct Node{
int to,next;
}e[N << 2];
int head[N],dist[N],tot,cnt[N];
const int MOD = 100003;
bool vis[N];
void add(int u,int v){
++tot;
e[tot].next = head[u];
e[tot].to = v;
head[u] = tot;
}
void bfs(){
queue<int> que;
que.push(1);
memset(dist,0x3f,sizeof dist);
dist[1] = 0;
vis[1] = 1;
cnt[1] = 1;
while(!que.empty()){
auto u = que.front();
que.pop();
for(int i=head[u];i;i=e[i].next){
int to = e[i].to;
if(!vis[to] && dist[to] > dist[u] + 1){
dist[to] = dist[u] + 1;
cnt[to] = cnt[u];
que.push(to);
vis[to] = 1;
}
else if(dist[to] == dist[u] + 1){
cnt[to] += cnt[u];
cnt[to] %= MOD;
}
}
}
}
void solve(){
cin >> n >> m;
while(m--){
int u,v;
cin >> u >> v;
add(u,v),add(v,u);
}
bfs();
for(int i=1;i<=n;++i) cout << cnt[i] << '\n';
}
signed main(){
std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int t;
t = 1;
while(t--)
solve();
return 0;
}
观光
题面
思路
和单纯的求最短路条数一样,求次短路条数的时候,要想同步更新,也需要满足拓扑序(即能更新当前状态的所有状态都已经被更新好了);
上面也分析过了,这题因为边权都是正的,有两点不用考虑;
- 不存在权值为0的环(有这种情况就无解了,有INF种)
- 不用考虑负的边权,可以用拓扑序的迪杰斯特拉;
Code
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
typedef long long ll;
const int N = 1e3 + 10,M = 1e4 + 10;
int tot,n,m,S,F;
struct Edge{
int next,to,val;
}e[M];
int head[N],dist[N][2],cnt[N][2];
bool vis[N][2];
void add(int u,int v,int w){
++tot;
e[tot].val = w;
e[tot].next = head[u];
e[tot].to = v;
head[u] = tot;
}
struct Point{
//type为0表示最短路 为1表示次短路
int id,type,dist;
//小根堆重载大于号
bool operator>(const Point& p) const{
return dist > p.dist;
}
};
int dijkstra(){
memset(dist,0x3f,sizeof dist);
memset(cnt,0,sizeof cnt);
memset(vis,0,sizeof vis);
priority_queue<Point,vector<Point>,greater<Point>> pq;
dist[S][0] = 0;
cnt[S][0] = 1;
pq.push({S,0,0});
while(!pq.empty()){
auto p = pq.top();
pq.pop();
int u = p.id,type = p.type;
if(vis[u][type]) continue;
vis[u][type] = 1;
for(int i=head[u];i;i=e[i].next){
int to = e[i].to;
int val = e[i].val;
if(dist[to][0] > dist[u][type] + val){
//最小值被覆盖 那么次小值接替最小值
dist[to][1] = dist[to][0];
cnt[to][1] = cnt[to][0];
pq.push({to,1,dist[to][1]});
dist[to][0] = dist[u][type] + val;
cnt[to][0] = cnt[u][type];
//更新过都要放入队列(迪杰斯特拉算法)
pq.push({to,0,dist[to][0]});
}
else if(dist[to][0] == dist[u][type] + val){
cnt[to][0] += cnt[u][type];
}
//注意这里不能if
else if(dist[to][1] > dist[u][type] + val){
dist[to][1] = dist[u][type] + val;
cnt[to][1] = cnt[u][type];
pq.push({to,1,dist[to][1]});
}
else if(dist[to][1] == dist[u][type] + val){
cnt[to][1] += cnt[u][type];
}
}
}
int ret = cnt[F][0];
if(dist[F][0] + 1 == dist[F][1]) ret += cnt[F][1];
return ret;
}
void solve(){
tot = 0;
memset(head,0,sizeof head);
cin >> n >> m;
for(int i=1;i<=m;++i){
int u,v,w;
cin >> u >> v >> w;
add(u,v,w);
}
cin >> S >> F;
cout << dijkstra() << '\n';
}
signed main(){
std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int t;
cin >> t;
while(t--)
solve();
return 0;
}
逛公园
题面
思路
还没看懂…