1. 程式人生 > >CH6202 黑暗城堡(最短路徑生成樹+二叉堆優化的dijkstra求最短路模板)

CH6202 黑暗城堡(最短路徑生成樹+二叉堆優化的dijkstra求最短路模板)

題目連結:

http://contest-hunter.org:83/contest/0x60%E3%80%8C%E5%9B%BE%E8%AE%BA%E3%80%8D%E4%BE%8B%E9%A2%98/6202%20%E9%BB%91%E6%9A%97%E5%9F%8E%E5%A0%A1

 

題意:求在一張無向圖中用到達各點的最短路生成的樹一共可以有多少顆。

分析:區別最小生成樹,用最短路構成的數不一定是最小生成樹,最小生成樹也不一定由最短路們構成

運用乘法原理,我們只需要求出到達圖中某點的路徑中路徑長度等於最短路長度的有多少條,並將其疊乘進ans中即可。

所以現用二叉堆優化的dijkstra求“1”到各點的最短路長度d【N】;然後: 

怎麼求出到達某點的這樣的路徑有多少條呢?我們只要遍歷一遍某點前面的點們,看看有沒有符合要求d【前面的點】+v(邊權值)=d【“1”點到該點的最短路】的點即可。前面的點指的是與當前點有邊相連且在到達當前點之前到達的點。我們可以在求出“1”點到達每個點的最短路之後根據dis對這些點進行排序。然後可達關係的後面的點與前面點的先後關係就是在排序後序列中的關係。PS:遍歷所有點、二層迴圈遍歷與當前點連線的所有點就好了,他後面的點的d加上兩點間邊的權值肯定不會是到達當前點的最短路,所以一定不會被選擇,emm,,,,,,也就說沒必要排序,可能時間複雜上稍有差異吧,不過應該也不大)。

AC程式碼:

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include<algorithm>
#include <set>
#include <queue>
#include <stack>
#include<vector>
#include<map>
#include<ctime>
#define ll long long
using namespace std;
const int N=100010,M=1000010;
int head[N];
int ver[M];
int edge[M];
int Next[M];
bool v[N];
int tot;
int d[N];
priority_queue< pair<int,int> >Q;
void add(int x,int y,int z)
{
    ver[++tot]=y;
    edge[tot]=z;
    Next[tot]=head[x];
    head[x]=tot;
}
void dijkstra()
{

    memset(d,0x3f,sizeof(d));
    memset(v,0,sizeof(v));
    d[1]=0;
    Q.push(make_pair(0,1));
    while(Q.size())
    {
        int x=Q.top().second;
        Q.pop();
        if(v[x])continue;
        v[x]=1;
        for(int i=head[x];i;i=Next[i])
        {
            int y=ver[i],z=edge[i];
            if(d[y]>d[x]+z)
            {
                d[y]=d[x]+z;
                Q.push(make_pair(-d[y],y));
            }
        }
    }
}
int num[10010];
int main()
{
    int n,m;
    while(cin>>n>>m)
    {

        tot=0;
        memset(head,0,sizeof(head));
        memset(ver,0,sizeof(ver));
        memset(edge,0,sizeof(edge));
        memset(Next,0,sizeof(Next));

        for(int i=1;i<=m;++i)
        {
            int x,y,l;
            cin>>x>>y>>l;
            add(x,y,l);//dijkstra用於無向圖,所以,別忘了建雙向邊;
            add(y,x,l);
        }
        dijkstra();//求最短路
        memset(num,0,sizeof(num));
        num[1]=1;
        for(int i=2;i<=n;++i)//求到達某點路徑中長度為最短路的路徑數目;
        {
            for(int j=head[i];j;j=Next[j])
            {
                int pp=ver[j],vv=edge[j];//鄰接表常規操作
                if(d[i]==d[pp]+vv)num[i]++;
            }
        }
        ll ans=1;
        for(int i=1;i<=n;++i)
        {
            ans=ans*num[i]%2147483647;//疊乘答案;
        }
        cout<<ans<<endl;



    }
}

 

The end;