P1345 [USACO5.4]奶牛的電信(點拆邊 + 網絡最小割)

題目描述
農夫約翰的奶牛們喜歡通過電郵保持聯繫,於是她們建立了一個奶牛電腦網絡,以便互相交流。這些機器用如下的方式發送電郵:如果存在一個由c臺電腦組成的序列a1,a2,…,a©,且a1與a2相連,a2與a3相連,等等,那麼電腦a1和a©就可以互發電郵。

很不幸,有時候奶牛會不小心踩到電腦上,農夫約翰的車也可能碾過電腦,這臺倒黴的電腦就會壞掉。這意味着這臺電腦不能再發送電郵了,於是與這臺電腦相關的連接也就不可用了。

有兩頭奶牛就想:如果我們兩個不能互發電郵,至少需要壞掉多少臺電腦呢?請編寫一個程序爲她們計算這個最小值。

以如下網絡爲例:

1* / 3 - 2*

這張圖畫的是有2條連接的3臺電腦。我們想要在電腦1和2之間傳送信息。電腦1與3、2與3直接連通。如果電腦3壞了,電腦1與2便不能互發信息了。

輸入格式 第一行
四個由空格分隔的整數:N,M,c1,c2.N是電腦總數(1<=N<=100),電腦由1到N編號。M是電腦之間連接的總數(1<=M<=600)。最後的兩個整數c1和c2是上述兩頭奶牛使用的電腦編號。連接沒有重複且均爲雙向的(即如果c1與c2相連,那麼c2與c1也相連)。兩臺電腦之間至多有一條連接。電腦c1和c2不會直接相連。

第2到M+1行 接下來的M行中,每行包含兩臺直接相連的電腦的編號。

輸出格式 一個整數表示使電腦c1和c2不能互相通信需要壞掉的電腦數目的最小值。

輸入輸出樣例

輸入 #1 複製
3 2 1 2
1 3
2 3
輸出 #1 複製
1

思路

題意:這一題是問cow需要最少破話多少臺電腦才能 才能使位於起點和終點位置的 cow 不能通信,也就是說這一題是讓拆的是,而不是讓拆的邊,我們不能夠直接使用,這一題如果是讓拆的是邊的話,那就很簡單,因爲不能夠通信,就意味着 在拆邊之後,圖的聯通性就沒了,圖被分割成兩部分,那麼我們所要求的是最小割。。。。。這一題是 割點 ,當我們在破壞掉一個u 之後(假設這一條邊爲 u——v),我們不能夠影響原來 v點的通與其他點的通信(除了u點),這一題如果我們能夠把個 割點——> 轉化割邊 就好解決了
思路:我們可以考慮把 一個點(假定編號爲 i)拆成兩個點,這兩個點的編號是 :i , i + n,在 i 與 i+1 點之間有一條權值爲 1 指向 i+1 點的邊(i+1這個點 和這個邊是我們要用 Add函數添加進去的) ,還有一點要明白,對於題上給的邊,所有與原來 i 所連的點 進入i的邊 還是i ,而原來 從 i 點出去的邊要變成 從 i + 1 點過出去的邊,一定是可以雙向通行,而且可以無線通信(所以題目上給的邊就是 INF),i 點拆分後的結果
在這裏插入圖片描述
當我們把所有的點之後,再把 題目上給的邊我們就得到了最終的圖:
在這裏插入圖片描述
上圖⬆️中 的 紅線:拆點之間的邊權爲 1 的線,黑線是題目給的邊權值爲 INF
最後最後:我麼要特殊考慮:源點s 與匯點e , 我們要明白 原點可以通過與 源點相連的邊無限發送消息,而不是一次,所以我們 這個時候 所用的原點就變成了 s + n(這個是因爲 拆點造成的),而匯點可以通過入邊無相接受消息,所以 入邊 鏈接的還是 e (即所用的匯點e不變)

題解(Dinic)

#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;

#define INF 0x3f3f3f3f
const int maxn = 205;
const int maxm = 2005;
struct Edge
{
    int v,w,next;
} edge[2*maxm];
int head[maxm],cur[maxm];
int deep[maxm];
int use[maxm];
int n,m,s,e;

int k = -1;
void Add(int u, int v, int w)
{
    edge[++ k] = (Edge){ v, w, head[u]}; head[u] = k;
    edge[++ k] = (Edge){ u, 0, head[v]}; head[v] = k;
}

//來分層用的
bool bfs(int s, int e)
{
    for(int i = 0; i <= 2*n; i ++)
        cur[i] = head[i], deep[i] = INF, use[i] = 0;

    deep[s] = 0;
    queue<int> q;
    q.push(s);
    int u,v;
    while(! q.empty())
    {
        u = q.front(); q.pop();
        use[u] = 0;

        for(int i = head[u]; i != -1; i = edge[i].next)
        {
            v = edge[i].v;
            if(edge[i].w && deep[v] == INF)
            {
                deep[v] = deep[u] + 1;
                if(! use[v])
                {
                    q.push(v);
                    use[v] = 1;
                }
            }
        }
    }
    if(deep[e] == INF)
        return false;
    return true;
}

int dfs(int now, int e, int limit)
{
    if(! limit || now == e) return limit;
    int flow = 0,f;
    for(int i = cur[now]; i != -1; i = edge[i].next)
    {
        if(deep[edge[i].v] == deep[now] + 1 && (f = dfs(edge[i].v, e, min(limit, edge[i].w))))
        {
            cur[now] = i;   //當前弧優化
            flow += f;
            limit -= f;
            edge[i].w -= f;
            edge[i^1].w += f;
            if(! limit)
                break;
        }
    }
    return flow;
}

int Dinic(int s, int e)
{
    int mx_flw = 0;
    while(bfs(s, e))
        mx_flw += dfs(s, e, INF);
    return mx_flw;
}


void init()
{
    k = -1;
    memset(head, -1, sizeof(head));
}

int main()
{
    ios::sync_with_stdio(false);
    //freopen("T.txt","r",stdin);
    cin >> n >> m >> s >> e;
    s += n;
    init();
    //拆點操作
    for(int i = 1; i <= n; i ++)
        Add(i, i + n, 1);
    int u, v;
    for(int i = 1; i <= m; i ++)
        cin >> u >> v, Add(u + n, v, INF), Add(v + n, u, INF);

    cout << Dinic(s, e) << endl;

    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章