Cap Ghep Do Thi 2 Phia

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 15

BỘ GHÉP CỰC ĐẠI TRÊN ĐỒ THỊ HAI PHÍA

1. Đồ thị hai phía

2. Bài toán ghép đôi không trọng số

1
2
3. Thuật toán tìm đường mở

3
4. Một số bài tập
Bài 1. MATCH1
Cho đồ thị hai phía G = (X U Y, E); Các đỉnh của X ký hiệu là x1, x2, ..., xm, các đỉnh của Y
ký hiệu là y1, y2, ..., yn. Một bộ ghép trên G là một tập các cạnh thuộc E đôi một không có đỉnh
chung.
Yêu cầu: Hãy tìm bộ ghép cực đại (có nhiều cạnh nhất) trên G.
Dữ liệu: Vào từ file văn bản MATCH1.inp
- Dòng 1: Chứa hai số m, n (1 ≤ m, n ≤ 100)
- Các dòng tiếp, mỗi dòng chứa hai số nguyên dương i, j cho biết thông tin về một cạnh (xi,
yj) thuộc E.
Kết quả: Ghi vào file văn bản MATCH1.out
- Dòng 1: Ghi số cạnh trong bộ ghép cực đại tìm được (K).
- K dòng tiếp theo, mỗi dòng ghi thông tin về một cạnh được chọn vào bộ ghép cực đại: Gồm
2 số u, v thể hiện cho cạnh nối (xu, yv).
Ví dụ:
MATCH1.inp MATCH1.out ĐỒ THỊ

4
45 4
11 11
14 24
21 33
22 42
24
32
33
42
43

Một số lưu ý:
- MY[i] lưu đỉnh trong tập Y ghép với đỉnh i của tập X
- Nếu đỉnh i chưa ghép với đỉnh nào thì MY[i] = 0
- MX[j] lưu đỉnh trong tập X ghép với đỉnh j của tập Y
- Nếu đỉnh j chưa ghép với đỉnh nào thì MX[j] = 0
- Nếu có cạnh ghép (i,j), i  X, j  Y thì MX[j] = i, MY[i] = j
Thuật toán tìm đường mở
- TR[j] cho biết đỉnh được thưm trước j trên đường mở
- OK, đúng nếu tìm thấy đường mở, sai nếu chưa tìm thấy đường mở

void dfs(int i)
{
for(int j=1; j<=n;j++)
{
if(a[i][j]==1 && TR[j]==0)
{
TR[j]=i;
if (MX[j]==0)
{
doi(j);
OK=true;
}
else dfs(MX[j]);
if(OK)return;
}
}
}

#include<bits/stdc++.h>
using namespace std;
vector<int> M[105];
int MX[105], MY[105], TR[105];
int a[105][105];

5
int m, n;
bool OK;
void doi(int j)
{
while (j!=0)
{
int i=TR[j];
int tam=MY[i];
MY[i]=j;
MX[j]=i;
j=tam;
}
}
void dfs(int i)
{
for(int j=1; j<=n;j++)
{
if(a[i][j]==1 && TR[j]==0)
{
TR[j]=i;
if (MX[j]==0)
{
doi(j);
OK=true;
}
else dfs(MX[j]);
if(OK)return;
}
}
}
void xuli()
{

for(int i=1; i<=m; i++)


{
OK=false;
memset(TR,0,sizeof(TR));
dfs(i);
}
int kq=0;
for(int i=1; i<=m; i++)
if (MY[i]!=0 ) kq++;
cout<<kq<<endl;
for(int i=1; i<=m; i++)
if (MY[i]!=0 ) cout<< i <<" "<<MY[i]<<endl;
}

6
int main()
{
//freopen("match1.INP","r",stdin);
//freopen("match1.OUT","w",stdout);
int u, v;
cin>>m>>n;
while(cin>>u>>v)
{
a[u][v]=1;
}
xuli();
return 0;
}
Bài 2. NKBM ( Giống bài MATCH1)
Trong lý thuyết đồ thị, một cặp ghép hay tập cạnh độc lập của một đồ thị là một tập các cạnh không
có đỉnh chung. Bài toán ghép cặp thường được quan tâm trong trường hợp đồ thị hai phía. Đồ thị
đơn vô hướng G=(V,E) là một đồ thị hai phía nếu như tồn tại một cách phân hoạch tập đinh V thành
hai tập V1, V2 sao cho mỗi cạnh thuộc E đều có dạng v1v2 với v1 thuộc V1, v2 thuộc V2. Một ví dụ đó
là bài toán sắp xếp công việc. Giả sử có P người và J công việc, mỗi người có thể làm một số công
việc nào đó. Ta mô hình bài toán bằng một đồ thị hai phía với P+J đỉnh. Nếu người p i có thể làm
công việc ji thì có một cạnh giữa hai đỉnh pi và ji trên đồ thị.
Tìm một cặp ghép cực đại (còn được gọi là cặp ghép có lực lượng lớn nhất) trên một đồ thị hai phía
G=(V=(X,Y), E) là một bài toán quen thuộc và đơn giản trong lý thuyết đồ thị. Định lý Konig chỉ ra
rằng trong một đồ thị hai phía, kích thước của cặp ghép cực đại bằng kích thước của phủ đỉnh nhỏ
nhất. Từ kết quả này, bài toán phủ đỉnh nhỏ nhất và bài toán tập độc lập lớn nhất trên đồ thị hai phía
có thể giải được trong thời gian đa thức.
Bạn hãy thử giải quyết bài toán tìm cặp ghép cực đại trên đồ thị hai phía: cho biết đồ thị hai phía
G=(V=(X,Y), E), hãy tìm cặp ghép cực đại (có nhiều cạnh nhất).
Dữ liệu: Vào từ file văn bản NKBM.inp
 Dòng đầu tiên chứa hai số x, y, m (x, y ≤ 1000), theo thứ tự là số đỉnh thuộc tập X, tập Y của
đồ thị và số cạnh nối.
 m dòng tiếp theo mỗi dòng ghi hai số i, j cách nhau bởi một dấu cách thể hiện có cạnh nối
giữa hai đỉnh xi, yj.
Kết quả: Ghi vào file văn bản NKBM.out
In ra kích thước của cặp ghép cực đại.
Ví dụ:

NKBM.inp NKBM.out
459
11 4
14
21
22
24
32
33
7
42
43

#include<bits/stdc++.h>
using namespace std;
vector<int> ke[200005];
int MX[200005], MY[200005], TR[200005];

int m, n,p;
bool OK;
void doi(int j)
{
while (j!=0)
{
int i=TR[j];
int tam=MY[i];
MY[i]=j;
MX[j]=i;
j=tam;
}
}
void dfs(int i)
{
for(int t=0; t<ke[i].size();t++)
{
int j=ke[i][t];
if(TR[j]==0)
{
TR[j]=i;
if (MX[j]==0)
{
doi(j);
OK=true;
}
else dfs(MX[j]);
if(OK)return;
}
}
}
void xuli()
{

for(int i=1; i<=m; i++)


{
OK=false;
memset(TR,0,sizeof(TR));

8
dfs(i);
}
int kq=0;
for(int i=1; i<=m; i++)

9
if (MY[i]!=0 ) kq++;
cout<<kq<<endl;

}
int main()
{
//freopen("match1.INP","r",stdin);
//freopen("match1.OUT","w",stdout);
int u, v;
cin>>m>>n>>p;
for(int i=1; i<=p; i++)
{
cin>>u>>v;
ke[u].push_back(v);
}
xuli();
return 0;
}

Bài 3. ASSIGN1 - Phân công hoàn thành sớm nhất

Có n người, n việc (1 < n ≤ 200). Người thứ i thực hiện công việc j mất C[i,j] đơn vị thời gian. Giả
sử tất cả bắt đầu vào thời điểm 0, hãy tìm cách bố trí mỗi công việc cho mỗi người sao cho thời điểm
hoàn thành công việc là sớm nhất có thể.
Dữ liệu: Vào từ file văn bản ASSIGN1.inp
- Dòng đầu: N
- Tiếp theo là ma trận C[i,j]. (thuộc kiểu Integer)
Kết quả: Ghi vào file văn bản ASSIGN1.out
- Ghi thời điểm sớm nhất hoàn thành.
Ví dụ:
ASSIGN1.inp ASSIGN1.out
4 5
10 10 10 2
10 10 3 10
4 10 10 10
10 5 10 10

Tất cả mọi người cùng có thời điểm bắt đầu công việc là như nhau (thời điểm 0), Thử phân n
công việc cho n người theo các mức thời gian tối ưu dần, Chặt nhị phân

#include<bits/stdc++.h>
using namespace std;
vector<int> M[1005];
int MX[1005], MY[1005], TR[1005];
int a[1005][1005];
10
int tg,n,kq=0;
bool OK;
void doi(int j)
{
while (j!=0)
{

int i=TR[j];
int tam=MY[i];
MY[i]=j;
MX[j]=i;
j=tam;
}
}
void dfs(int i)
{
for(int j=1; j<=n;j++)
{
if(a[i][j]<=tg && TR[j]==0)
{
TR[j]=i;
if (MX[j]==0)
{
doi(j);
OK=true;
}
else dfs(MX[j]);
if(OK)return;
}
}
}
void ghepcap()
{

for(int i=1; i<=n; i++)


{
OK=false;
memset(TR,0,sizeof(TR));
dfs(i);
}
}

void xuli()
{
int l=0, r=1000000000;
while (l<=r)

11
{
memset(MX,0,sizeof(MX));
memset(MY,0,sizeof(MY));
tg=(l+r)/2;
ghepcap();
int socap=0;
for(int i=1; i<=n; i++)
if (MY[i]!=0 ) socap++;
if (socap==n)
{
kq=tg;
r=tg-1;
}
else l=tg+1;
}
cout<<kq;
}

int main()
{
//freopen("match1.INP","r",stdin);
//freopen("match1.OUT","w",stdout);
cin>>n;
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++) cin>>a[i][j];
xuli();
return 0;
}

12
Bài 4. FMATCH
FJ có N (1 ≤ N ≤ 50,000) cô bò và M (1 ≤ M ≤ 50,000) chú bò. Cho danh sách P (1 ≤ P ≤ 150,000) khả năng ghép
đôi giữa các cô bò và chú bò, hãy tính số cặp lớn nhất có thể ghép được. Tất nhiên, một cô bò có thể ghép với
tối đa là một chú bò và ngược lại.

Input
Dòng đầu tiên chứa 3 số nguyên, N, M, và P. Mỗi dòng trong số P dòng tiếp theo chứa 2 số nguyên A (1 ≤ A ≤
N) và B (1 ≤ B ≤ M), thể hiện việc cô bò A có thể ghép được với chú bò B.

Output
In ra một số nguyên thể hiện số cặp lớn nhất có thể đạt được.

Example
Input:
5 4 6
5 2
1 2
4 3
3 1
2 2
4 4

Output:
3

Cô bò 1 có thể được ghép với chú bò 2, cô bò 3 với chú bò 1, và cô bò 4 với chú bò 3.

Sử dụng thuat toán tham lam: Tìm bộ ghép cực đại tối thiểu, sau đó mở rộng bộ ghép từ
các đường mở

- Với mỗi đỉnh trong X tìm 1 đỉnh trong Y có thể nối với nó để tạo ra 1 cặp ghép
- Có bao nhiêu cặp ghép được tạo ra tự nhiện, đây là bộ ghép tối thiệu
- Tìm đường mở để mở rộng bộ ghép
Ví dụ:

13
#include<bits/stdc++.h>
using namespace std;
vector<int> ke[150000];
int MX[150000], MY[150000], TR[150000];
int m, n,p;
bool OK;

void dfs(int i)
{
for(int t=0; t<ke[i].size();t++)
{
int j=ke[i][t];
if(TR[j]==0)
{
TR[j]=i;
if (MX[j]!=0) dfs(MX[j]);
else OK=true;
if(OK){ MX[j]=i; return;}
}
}
}
void xuli()
{
int i,nchg,nchg_c,chg[150000];
for (int i=1; i<=m; i++) chg[i]=i;
nchg=m;
memset(MX,0,sizeof(MX));
14
do
{
nchg_c=nchg;
memset(TR,0,sizeof(TR));
for (int i=nchg; i>=1; i--)
{
OK=false;
dfs(chg[i]);
if (OK)
{
chg[i]=chg[nchg];
nchg--;
}
}

}
while (nchg!=nchg_c);

int kq=0;
for(int j=1; j<=n; j++)
if (MX[j]!=0 ) kq++;
cout<<kq<<endl;
}

int main()
{
ios_base::sync_with_stdio(0);
cin.tie(0);
//freopen("match1.INP","r",stdin);
//freopen("match1.OUT","w",stdout);
int u, v;
cin>>m>>n>>p;
for(int i=1; i<=p; i++)
{
cin>>u>>v;
ke[u].push_back(v);
}
xuli();
return 0;
}

15

You might also like