1. 程式人生 > >[BZOJ] 1491 [洛谷] P2047 [NOI2007] 社交網路

[BZOJ] 1491 [洛谷] P2047 [NOI2007] 社交網路

傳送門——洛谷

傳送門——BZOJ

Description

在社交網路(socialnetwork)的研究中,我們常常使用圖論概念去解釋一些社會現象。不妨看這樣的一個問題。

在一個社交圈子裡有n個人,人與人之間有不同程度的關係。我們將這個關係網路對應到一個n個結點的無向圖上,

兩個不同的人若互相認識,則在他們對應的結點之間連線一條無向邊,並附上一個正數權值c,c越小,表示兩個人

之間的關係越密切。我們可以用對應結點之間的最短路長度來衡量兩個人s和t之間的關係密切程度,注意到最短路

徑上的其他結點為s和t的聯絡提供了某種便利,即這些結點對於s和t之間的聯絡有一定的重要程度。我們可以通過

統計經過一個結點v的最短路徑的數目來衡量該結點在社交網路中的重要程度。考慮到兩個結點A和B之間可能會有

多條最短路徑。我們修改重要程度的定義如下:令Cs,t表示從s到t的不同的最短路的數目,Cs,t(v)表示經過v從s

到t的最短路的數目;則定義

為結點v在社交網路中的重要程度。為了使I(v)和Cs,t(v)有意義,我們規定需要處理的社交網路都是連通的無向圖

,即任意兩個結點之間都有一條有限長度的最短路徑。現在給出這樣一幅描述社交網路的加權無向圖,請你求出每

一個結點的重要程度。

Input

輸入第一行有兩個整數n和m,表示社交網路中結點和無向邊的數目。在無向圖中,我們將所有結點從1到n進行編號

。接下來m行,每行用三個整數a,b,c描述一條連線結點a和b,權值為c的無向邊。注意任意兩個結點之間最多有

一條無向邊相連,無向圖中也不會出現自環(即不存在一條無向邊的兩個端點是相同的結點)。n≤100;m≤4500 

,任意一條邊的權值 c 是正整數,滿足:1≤c≤1000。所有資料中保證給出的無向圖連通,且任意兩個結點之間

的最短路徑數目不超過 10^10

Output

輸出包括n行,每行一個實數,精確到小數點後3位。第i行的實數表示結點i在社交網路中的重要程度。

Sample Input

4 4
1 2 1
2 3 1
3 4 1
4 1 1

Sample Output

1.000
1.000
1.000
1.000

HINT

 

社交網路如下圖所示。

 

 

對於 1 號結點而言,只有 2 號到 4 號結點和 4 號到 2 號結點的最短路經過 1 號結點,而 2 號結點和 4 號結

 

點之間的最短路又有 2 條。因而根據定義,1 號結點的重要程度計算為 1/2 + 1/2 = 1 。由於圖的對稱性,其他

 

三個結點的重要程度也都是 1 。

 

 

 

這是一道年代久遠的NOI水題,我們可以明確地知道這道題要用最短路來寫。

既然n<=100所以直接用floyd就可以寫,而且程式碼量很小。

具體來說:

我們需要新增一個數組road記錄下從點i到點j的最短路的數量。

先初始化陣列,若兩個點i,j之間有連邊,那麼road[i][j]=1。由於是無向圖,road[j][i]=1。

在跑floyd的過程中,我們在更新最短路長度的同時,同步更新最短路徑的數量。

if(Map[i][j]>Map[i][k]+Map[k][j])//如果發現更優的線路,那麼最短路徑數就更新為
{                                //i到k的最短路徑數*j到k的最短路徑數。
    Map[i][j]=Map[i][k]+Map[k][j];//至於為什麼是*可以去問下你的初中數學老師。
    road[i][j]=road[i][k]*road[k][j];
}
else if(Map[i][j]==Map[i][k]+Map[k][j])//如果發現了另一條同樣優的最短路
    road[i][j]+=road[i][k]*road[k][j];//那麼最短路徑數就加上i到k的路徑數*k到j的路徑數。

跑完之後就是一個模擬的過程了。

列舉每一個點,再列舉所有的起點和終點,將經過這個點的路徑數加起來再除掉總路徑數。

for(int k=1;k<=n;k++)
{
    double ans=0;
    for(int j=1;j<=n;j++)
        for(int i=1;i<=n;i++)
            if(k!=i&&k!=j&&j!=i)
                if(Map[i][j]==Map[i][k]+Map[k][j]&&road[i][j]!=0)
                    ans+=(double)road[i][k]*road[k][j]/road[i][j];
    printf("%.3f\n",ans);//和上面floyd的過程其實挺像的。
}

所以說洛谷的難度標籤還是不可信的,這道題明顯只有普及組的難度。

下面放出完整程式碼供大家抄襲參考。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
long long Map[1005][1005],road[1005][1005];
long long x,y,z,n,m;
int main()
{
    cin>>n>>m;
    memset(Map,11,sizeof(Map));
    for(int i=1;i<=m;i++)
    {
        cin>>x>>y>>z;
        Map[x][x]=0;
        Map[y][y]=0;
        Map[x][y]=min(Map[x][y],z);
        Map[y][x]=Map[x][y];
        road[x][y]=1;
        road[y][x]=1;
    }
    for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
            {
                if(Map[i][j]>Map[i][k]+Map[k][j])
                {
                    Map[i][j]=Map[i][k]+Map[k][j];
                    road[i][j]=road[i][k]*road[k][j];
                }
                else if(Map[i][j]==Map[i][k]+Map[k][j])
                    road[i][j]+=road[i][k]*road[k][j];
            }
    for(int k=1;k<=n;k++)
    {
        double ans=0;
        for(int j=1;j<=n;j++)
            for(int i=1;i<=n;i++)
                if(k!=i&&k!=j&&j!=i)
                    if(Map[i][j]==Map[i][k]+Map[k][j]&&road[i][j])
                        ans+=(double)road[i][k]*road[k][j]/road[i][j];
        printf("%.3f\n",ans);
    }
    return 0;
}