天天看點

[CTSC 2008] 祭祀

[題目連結]

         https://www.lydsy.com/JudgeOnline/problem.php?id=1143

[算法]

          答案為最小路徑可重複點覆寫所包含的路徑數,将原圖G進行弗洛伊德傳遞閉包,得到一張新圖G',然後求出拆點二分圖G2'的最大比對,N - 最大比對 即為答案,我們嘗試證明上述結論 :

          設祭祀點集合為S,最小路徑可重複點覆寫的邊集為Path,由于Path覆寫了所有節點,故每條路徑上至多選一個點,有 : |S| <= |Path| , 是以,如果我們能構造出一組解,使得| S | = | Path | , 就證明了此結論,這裡給出一種構造方案 :

          首先求出拆點二分圖的最大比對,設節點x在拆點二分圖上分别對應左部節點x和右部節點x' ,  對于每個非比對節點x0,我們不斷通路 x0,match[x0'],match[ match[x0'] ] .. 直到最後遇到一個左部節點y0,使得其右部點y0'為非比對點, 那麼就得到了一條路徑, 其中y0為起點,x0為

          終點,求出這樣的所有路徑,就得到了| Path |的一種方案,且所有路徑不相交,我們現在要将| Path |集合中的每條路徑選出一個節點,構成集合| S |

          首先我們将所有路徑的終點構成一個集合E,根據傳遞閉包的性質,兩個祭祀點之間無路徑相連,等價于在新圖G’上任意兩個祭祀點之間沒有邊,不妨讓集合E中的每個節點走一條邊,構成集合Next(E),如果E和Next(E)的交集為空集,則S = E

          否則,對于交集中的每個點e,我們沿着e所在的路徑不斷向上移動,直到e不在目前的交集中,從E中删除e,加入e',重複以上過程,直到交集為空,就求出了S的一種組成方案

          可以證明,在任何時刻,我們都能找到合法的e',因為若沒有,說明e所在的路徑上所有點都可以被其他路徑上的點到達,我們可以找到到達e所在的的路徑起點的那條路徑,将其延伸,使得| Path | 減少1,并覆寫所有節點,與Path的最小性沖突

          綜上所述,答案即為最小路徑可重複點覆寫所包含的路徑數

   [代碼]

#include<bits/stdc++.h>
using namespace std;
#define MAXN 210

int i,j,k,n,m,u,v,ans;
bool g[MAXN][MAXN],mp[MAXN][MAXN];
bool visited[MAXN];
int match[MAXN];

inline bool hungary(int u)
{
        int v;
        for (v = 1; v <= n; v++)
        {
                if (mp[u][v] && !visited[v])
                {
                        visited[v] = true;
                        if (!match[v] || hungary(match[v]))
                        {
                                match[v] = u;
                                return true;
                        }
                }
        }
        return false;
}

int main() 
{
        
        scanf("%d%d",&n,&m);
        for (i = 1; i <= n; i++) g[i][i] = true;
        for (i = 1; i <= m; i++)
        {
                scanf("%d%d",&u,&v);
                g[u][v] = true;
        }
        for (k = 1; k <= n; k++)
        {
                for (i = 1; i <= n; i++)
                {
                        for (j = 1; j <= n; j++)
                        {
                                g[i][j] |= g[i][k] & g[k][j];
                        }
                }
        }
        for (i = 1; i <= n; i++)
        {
                for (j = 1; j <= n; j++)
                {
                        if (i != j && g[i][j])
                           mp[i][j] = true;
                }
        }
        ans = n;
        for (i = 1; i <= n; i++)
        {
                memset(visited,false,sizeof(visited));
                if (hungary(i)) ans--;
        }
        printf("%d
",ans);
        
        return 0;
    
}