天天看點

P2764 最小路徑覆寫問題

這個題是一種題型,其實也就是拆一下點。

分析:

我們首先将原圖用n條路徑覆寫,每條邊隻經過每個節點。

現在盡量合并更多的路徑(即将兩個路徑通過一條邊首尾相連)。

可以知道,每合并兩條路徑,圖中的路徑覆寫數就會減少1。

是以我們隻需要利用網絡流合并相關的路徑即可。

答案求解:

首先将每個節點拆成(Xi,Yi)兩個節點,建立源點和彙點,分别連接配接(S,Xi)和(Yi,T)。

然後對于每一條原圖中的邊,建立邊(Xi,Yi)即可。

這樣每一條增廣路都隻會經過2個節點(Xa,Yb),對應合并的兩個節點。

由于每個節點至多與一個節點合并,故邊(S,Xi)和(Yi,T)容量為1。

此時的最大流對應的就是最多可以合并的路徑數。

方案輸出:

由于本人沒有想到什麼好的輸出方法,故隻能比較蠢地根據網絡流的殘餘流量構造每一條路徑(利用并查集維護路徑起點),然後從起點遞歸輸出。

題幹:

題目描述

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

提示:設 V={1,2,...,n}V={1,2,...,n}V={1,2,...,n} ,構造網絡 G1={V1,E1}G_1={V_1,E_1}G1​={V1​,E1​} 如下:

V1={x0,x1,...,xn}∪{y0,y1,...,yn}V_1={x_0,x_1,...,x_n}cup{y_0,y_1,...,y_n}V1​={x0​,x1​,...,xn​}∪{y0​,y1​,...,yn​}

E1={(x0,xi):i∈V}∪{(yi,y0):i∈V}∪{(xi,yj):(i,j)∈E}E_1={(x_0,x_i):iin V}cup{(y_i,y_0):iin V}cup{(x_i,y_j):(i,j)in E}E1​={(x0​,xi​):i∈V}∪{(yi​,y0​):i∈V}∪{(xi​,yj​):(i,j)∈E}

每條邊的容量均為 111 ,求網絡 G1G_1G1​ 的 (x0,y0)(x_0,y_0)(x0​,y0​) 最大流。
輸入輸出格式
輸入格式:

第一行有 222 個正整數 nnn 和 mmm 。 nnn 是給定GAP	ext{GAP}GAP(有向無環圖) GGG 的頂點數, mmm 是 GGG 的邊數。接下來的 mmm 行,每行有兩個正整數 iii 和 jjj 表示一條有向邊 (i,j)(i,j)(i,j)。

輸出格式:

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

代碼:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<ctime>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;
#define duke(i,a,n) for(register int i = a;i <= n;++i)
#define lv(i,a,n) for(register int i = a;i >= n;--i)
#define clean(a) memset(a,0,sizeof(a))
const int INF = 1 << 30;
typedef long long ll;
typedef double db;
template <class T>
void read(T &x)
{
    char c;
    bool op = 0;
    while(c = getchar(), c < '0' || c > '9')
        if(c == '-') op = 1;
    x = c - '0';
    while(c = getchar(), c >= '0' && c <= '9')
        x = x * 10 + c - '0';
    if(op) x = -x;
}
template <class T>
void write(T x)
{
    if(x < 0) putchar('-'), x = -x;
    if(x >= 10) write(x / 10);
    putchar('0' + x % 10);
}
const int N = 1e5 + 5;
struct node
{
    int l,r,nxt,w,oth;
}a[N * 10];
int len = 0,lst[N],m;
void add(int x,int y,int w)
{
//    cout<<x<<" "<<y<<" "<<w<<endl;
    int k1,k2;
    a[++len].l = x;
    a[len].r = y;
    a[len].w = w;
    a[len].nxt = lst[x];
    lst[x] = len;
    k1 = len;
    a[++len].l = y;
    a[len].r = x;
    a[len].w = 0;
    a[len].nxt = lst[y];
    lst[y] = len;
    k2 = len;
    a[k1].oth = k2;
    a[k2].oth = k1;
}
int n,p,q;
int h[N],ed = 0;
bool bfs()
{
    clean(h);
    h[0] = 1;
    queue <int> q;
    q.push(0);
    while(!q.empty())
    {
        int x = q.front();
        q.pop();
        for(int k = lst[x];k != -1;k = a[k].nxt)
        {
            int y = a[k].r;
            if(a[k].w > 0 && h[y] == 0)
            {
                h[y] = h[x] + 1;
                q.push(y);
            }
        }
    }
    if(h[ed] > 0)
    return true;
    else
    return false;
}
int find(int x,int f)
{
    if(x == ed)
    {
        return f;
    }
    int s = 0,t;
    for(int k = lst[x];k != -1;k = a[k].nxt)
    {
        int y = a[k].r;
        if(s < f && h[y] == h[x] + 1 && a[k].w > 0)
        {
            t = find(y,min(a[k].w,f - s));
            s += t;
            a[k].w -= t;
            a[a[k].oth].w += t;
        }
    }
    if(s == 0)
    h[x] = 0;
    return s;
}
int fa[N];
int find(int x)
{
    if(fa[x] == x) return x;
    return fa[x] = find(fa[x]);
}
void work(int x)
{
    printf("%d ",x);
    for(int k = lst[x];k != -1;k = a[k].nxt)
    {
        int y = a[k].r;
        if(y > n && a[k].w == 0)
        work(y - n);
    }
}
int main()
{
    read(n);read(m);
    memset(lst,-1,sizeof(lst));
    duke(i,1,n)
    add(0,i,1);
    ed = 2 * n + 1;
    duke(i,1,n)
    {
        add(n + i,ed,1);
    }
    duke(i,1,m)
    {
        int x,y;
        read(x);read(y);
        add(x,y + n,1);
    }
    int s = 0;
    while(bfs() == true)
    {
        s += find(0,INF);
    }
    duke(i,1,n)
    fa[i] = i;
    duke(i,1,len)
        if(a[i].l >= 1 && a[i].l <= n && a[i].r > n && a[i].w < ed && a[i].w == 0)
            fa[find(a[i].r - n)] = find(a[i].l);
    duke(i,1,n)
    {
        if(fa[i] == i)
        {
            work(i);
            puts("");
        }
    }
    printf("%d
",n - s);
    return 0;
}