題目描述
農夫約翰的奶牛們喜歡通過電郵保持聯繫,於是她們建立了一個奶牛電腦網絡,以便互相交流。這些機器用如下的方式發送電郵:如果存在一個由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;
}