1. 程式人生 > >洛谷 P2764 解題報告

洛谷 P2764 解題報告

size cup AS 每一個 二分圖匹配 add code 路徑 dig

P2764 最小路徑覆蓋問題

問題描述:

給定有向圖\(G=(V,E)\)。設\(P\)\(G\) 的一個簡單路(頂點不相交)的集合。如果\(V\) 中每個頂點恰好在\(P\) 的一條路上,則稱\(P\)\(G\) 的一個路徑覆蓋。\(P\) 中路徑可以從\(V\) 的任何一個頂點開始,長度也是任意的,特別地,可以為\(0\)\(G\) 的最小路徑覆蓋是\(G\)的所含路徑條數最少的路徑覆蓋。設計一個有效算法求一個有向無環圖\(G\) 的最小路徑覆蓋。

提示:設\(V={1,2,.... ,n}\),構造網絡\(G_1=(V_1,E_1)\)

如下:
\(V_1=\{x_1,x_2,...,x_n\}\cup\{y_1,y_2,...y_n\}\)


\(E_1=\{(x_0,x_i):i\in V\}\cup\{(y_0,y_i):i\in E \}\cup\{(x_i,y_j):(i,j)\in E\}\)

即每條邊的容量均為1。求網絡\(G_1\)最大流。

輸入輸出格式

輸入格式:

件第1 行有2個正整數\(n\)\(m\)\(n\)是給定有向無環圖\(G\) 的頂點數,\(m\)\(G\) 的邊數。接下來的\(m\)行,每行有\(2\) 個正整數\(i\)\(j\),表示一條有向邊\((i,j)\)

輸出格式:

從第1 行開始,每行輸出一條路徑。文件的最後一行是最少路徑數。

說明

\(1<=n<=150,1<=m<=6000\)


由@zhouyonglong提供SPJ


其實提示說的很清楚了。

這裏用我自己的感性語言解釋一下。

描述:將圖中每個點拆成兩個,分成兩個圖。把連原來的邊連上。跑二分圖匹配,最小路徑數=總點數-最大匹配數

解釋:
不難發現,路徑數+路徑集合中邊的數量=總點數。(肽鏈)

總點數不變,我們就可以轉化到求最大的邊的數量。

而對於原圖中的每一個點\(i\),都可以分成以下四中情況。

技術分享圖片

為了使邊的數量盡量大,我們應該多令情況(3)出現。

而這幾種情況中又一個點最多戳某一個點屁股,也只能被最多被一個戳。

那麽,劈配? 匹配?

再看看我們跑的二分圖是什麽,是不是很明了~


CODE:

#include <cstdio>
#include <cstring>
const int N=160;
int n,m;
struct edge
{
    int to,next;
}g[N*N];
int head[N],cnt=0;
void add(int u,int v)
{
    g[++cnt].to=v;
    g[cnt].next=head[u];
    head[u]=cnt;
}
int used[N],match[N];
bool m_find(int u)
{
    for(int i=head[u];i;i=g[i].next)
    {
        int v=g[i].to;
        if(!used[v])
        {
            used[v]=1;
            if(!match[v]||m_find(match[v]))
            {
                match[v]=u;
                return true;
            }
        }
    }
    return false;
}

void dfs(int now)
{
    if(!match[now]) {printf("%d ",now);return;}
    dfs(match[now]); printf("%d ",now);
}

int main()
{
    scanf("%d%d",&n,&m);
    int u,v;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&u,&v);
        add(u,v);
    }
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        memset(used,0,sizeof(used));
        if(m_find(i)) ans++;
    }
    memset(used,0,sizeof(used));
    for(int i=1;i<=n;i++)
        used[match[i]]=1;
    for(int i=1;i<=n;i++)
        if(!used[i])
        {dfs(i);printf("\n");}
    printf("%d\n",n-ans);
    return 0;
}

2018.5.6

洛谷 P2764 解題報告