1. 程式人生 > >洛谷Oj-P1144 最短路計數-Dijkstra + 遞推

洛谷Oj-P1144 最短路計數-Dijkstra + 遞推

問題描述:
給出一個N個頂點M條邊的無向無權圖,頂點編號為1~N。問從頂點1開始,到其他每個點的最短路有幾條。
AC程式碼:

typedef pair<int,int> Pair;//到源點的距離、頂點編號
priority_queue<Pair,vector<Pair>,greater<Pair> > pq;//小頂堆
struct edge
{
    int to;
    int next;
    int w;
};
edge e[2000010];
int head[1000010];
int id = 1;

int n,m;
int dis[1000010
];//距離 int cnt[1000010];//cnt[i]表示從源點到頂點i的最短路的條數 void add_edge(int u,int v,int w) { e[id].to = v; e[id].next = head[u]; e[id].w = w; head[u] = id; id++; } void dijkstra(int s)//堆優化 { memset(dis,0x3F,sizeof(dis));//初始化 dis[s] = 0;//初始化 cnt[1] = 1;//遞推邊界 pq.push(make_pair(dis[s],s));//將源點入堆
while(!pq.empty()) { int v = pq.top().second; pq.pop(); for(int i = head[v]; i != -1; i = e[i].next) { int to = e[i].to; int w = e[i].w; if(dis[to] == dis[v] + w)//如果和當前的最短路長度相等 cnt[to] = (cnt[to] + cnt[v]) % Mod;//則加上cnt[中轉點v]
if(dis[to] > dis[v] + w)//如果比當前的最短路還要短 { dis[to] = dis[v] + w;//更新 cnt[to] = 0;//置0 cnt[to] = (cnt[to] + cnt[v]) % Mod;//加上cnt[中轉點v] pq.push(make_pair(dis[to],to));//因為dis[to]被鬆弛了,再入堆 } } } return; } int main() { cin >> n >> m; memset(head,-1,sizeof(head));//初始化 for(int i = 1; i <= m; ++i) { int a,b; scanf("%d%d",&a,&b); if(a == b)//去掉自環 continue; add_edge(a,b,1); add_edge(b,a,1); } dijkstra(1); for(int i = 1; i <= n; ++i) cout << cnt[i] << endl; return 0; }

解決方法:
因為路徑的長度都是1,考慮用鄰接矩陣的冪次去做,逐次累乘,判斷是否可達,答案為第一次可達時的 a11a12a1n 。但是看到資料規模,這個方法算是沒戲了。
再就是考慮用最短路演算法,比如Dijkstra去做(為了跑得快一點,加了堆優化),然後在用中轉點鬆弛這一步上做文章,被鬆弛了說明要推翻之前的最短路條數,恰好相等則要加上,就這樣遞推。對於每一箇中轉點來說,它到源點的路徑長度已經是最短的了,不會再被鬆弛,最短路的條數也確定了。