CSP-Week7 ProblemA Floyd算法和傳遞性解題

CSP-Floyd算法和傳遞性解題

知識簡述

Floyd算法是圖論算法中求解全源最短路問題的權威算法。雖然其具有較高的O(n^3)複雜度,但是由於全源的特性和動態規劃的求解思路,在解決全局問題(有負邊無負環)時有較好的效果。受制於篇幅,此處不再講述動態規劃的證明問題,後續會退出動態規劃專題進行相關的詳解。
在這裏插入圖片描述按照上面的步驟,我們可以得出下列的Floyd基本實現代碼:

void Floyd(int n,int dis**)
{
    for(int k=1;k<=n;k++)//外面k層是更新的次數
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
}

在實現時,要注意到的問題是,調整次數k在最外層,如果你記錯了順序,很可能得到錯誤的距離矩陣。
Floyd算法能夠解決的問題:
1、求取圖上任意兩點間的關係(是否連通)
2、多元負權最短路,求解圖上任意兩點間的距離
3、求解圖上的閉包,求解圖上兩點間的傳遞閉包關係

題目概述

衆所周知,TT 有一隻魔法貓。
這一天,TT 正在專心致志地玩《貓和老鼠》遊戲,然而比賽還沒開始,聰明的魔法貓便告訴了 TT 比賽的最終結果。TT 非常詫異,不僅詫異於他的小貓咪居然會說話,更詫異於這可愛的小不點爲何有如此魔力?
魔法貓告訴 TT,它其實擁有一張遊戲勝負表,上面有 N 個人以及 M 個勝負關係,每個勝負關係爲 A B,表示 A 能勝過 B,且勝負關係具有傳遞性。即 A 勝過 B,B 勝過 C,則 A 也能勝過 C。
TT 不相信他的小貓咪什麼比賽都能預測,因此他想知道有多少對選手的勝負無法預先得知,你能幫幫他嗎?

INPUT&輸入樣例

第一行給出數據組數。
每組數據第一行給出 N 和 M(N , M <= 500)。
接下來 M 行,每行給出 A B,表示 A 可以勝過 B。
輸入樣例:

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

OUTPUT&輸出樣例

對於每一組數據,判斷有多少場比賽的勝負不能預先得知。注意 (a, b) 與 (b, a) 等價,即每一個二元組只被計算一次。
輸出樣例:

0
0
4

題目重述

給定直接的勝負關係,在閉包的範圍內進行勝負關係的傳導。
規則:A>B&B>C --> A>C(兩隊之間的勝負關係只存在一種A>B或A<B)
現要求對所有閉包內的進行傳遞求解,並確定出有多少個閉包關係無法確定

思路概述

通讀題目我們可以將題目總結如下:給定閉包的元素和閉包的已知關係,利用勝負關係進行閉包傳遞,最終確定出全部閉包關係。設計閉包和全源問題,自然而然就會想到Floyd算法,但是如果採用:暴力調整n^3次,遍歷求取無法確定的關係個數這種方法的話,在邊數較多時會出現TLE問題,因此對Floyd進行一定的優化至關重要。
根據對傳遞閉包的原理我們可以知道,只有滿足A>B的前提下,滿足第二個條件B>C,才能通過傳遞得出需要調整的關係A>C。如果在遍歷過程中發現A與B關係不已知,可以進行剪枝優化,不再進行內層的遍歷。利用這種方法,我們可以獲取理想的時間性能,從而完成任務。

總結

Floyd算法的實現雖然簡單,但是其體現了動態規劃系列算法路徑調整規劃過程的美感。由於Floyd本身並不複雜且時間複雜度比較高,在比賽中常用到的是對Floyd算法進行一定的修改和優化剪枝,從而設計出能夠滿足要求複雜度的算法。因此,在學習Floyd算法的過程中,不應該只關注於算法本身,而是將其靈活應用,並總結出一些常用的剪枝方法,從而使Floyd能夠取得較好的時間性能。

問題源碼(C++)

#include<iostream>
#include<stdio.h>
using namespace std;
const int M=5e2+1;
int dis[M][M];
int point_num;
int edge_num;
int floyd()
{
    int cnt=0;
    for(int k=1;k<=point_num;k++)
        for(int i=1;i<=point_num;i++)
        {
        	if(dis[i][k]!=1)
        	{ continue;}
            for(int j=1;j<=point_num;j++)
            {
                if(dis[i][k]==1 && dis[k][j]==1 && dis[i][j]==0)
                {
                    dis[i][j]=1;
                    cnt++;
                }
        	}
        }
    return cnt;
}
int main()
{
    int group_num=0;
    cin>>group_num;
    for(int i=1;i<=group_num;i++)
    {
    	int a=0;
    	int b=0;
    	int cnt=0; 
        cin>>point_num>>edge_num;
        for(int j=1;j<=point_num;j++)
            for(int k=1;k<=point_num;k++)
                dis[j][k]=0;
        for(int j=0;j<edge_num;j++)
        {
            cin>>a>>b;
            dis[a][b]=1;
            cnt++;
        }
        cnt+=floyd();
        int res=(point_num*(point_num-1)/2)-cnt;
        cout<<res<<endl;;     
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章