레야몬
[C++] 3683번 고양이와 개 - 이분 매칭 본문
1. 문제
- 모든 사용자는 다음 라운드에 진출시킬 동물과 이번 라운드에 떨어뜨릴 동물을 한 마리씩 투표한다. 모든 투표는 고양이 하나와 개 하나로 이루어져 있다. 각 시청자가 자신이 진출하는 동물로 뽑은 동물이 진출하고, 탈락한 동물로 뽑은 동물이 탈락되었을 때 "투표가 반영되었다."라고 한다. PD는 임의로 진출시키고 탈락시킬 동물을 여러 마리 골라 투표가 반영된 시청자 수를 최대화하려고 한다. 이때 최댓값을 구하시오.
<입력>
- -1- 테스트 케이스의 개수 \(T(1 \leq T \leq 100)\)
- --1- 고양이의 수 c, 개의 수 d, 투표한 시청자의 수 v \((1 \leq c, d \leq 100, 0 \leq v \leq 500)\)
- --2~v+1- 각 시청자의 투표정보가 주어진다. 첫 번째는 진출시킬 동물로 뽑은 동물, 두 번째는 탈락시킬 동물로 뽑은 동물. 고양이는 C, 개는 D로 시작하고 그 뒤 숫자는 동물의 번호이며 이는 1이상 c, d 이하이다.
<출력>
- 각 테스트 케이스 마다, 투표가 반영되는 시청자의 최댓값을 구하여라.
2. 재정의
- 각 동물의 진출 여부에 따라 이에 만족하는 사용자의 최대 수를 구하여라.
3. 해결방법
- 사용자들의 투표가 모두 만족한다고 가정했을 때 사용자들의 투표가 서로 충돌하게 된다. 이때 특정 사용자들의 투표를 무시함으로써 이 충돌을 막을 수가 있다. 즉 투표를 무시한 최소 횟수를 투표 횟수에 빼주면 투표가 반영되는 시청자의 최댓값이 되는 것이다.
- 고양이를 좋아하는 사람끼리는 서로 충돌할 수 없으므로 고양이를 좋아하는 사람과 개를 좋아하는 사람끼리 고려하자.
- 사용자들의 투표 결과 중 i번째 시청자의 좋아하는 동물과 j번째 시청자가 싫어하는 동물이 같거나, i번째 시청자의 싫어하는 동물, j번째 시청자가 좋아하는 동물이 같다면 모순이 발생하게 된다. 이를 통해 서로 모순 관계인 시청자들의 의견을 찾을 수 있고 이를 연결하여 고양이를 좋아하는 그룹과, 개를 좋아하는 그룹끼리 나누면 그래프가 만들어진다.
- 이때 모순을 뜻하는 간선을 제거해 충돌을 없애야 하고 이는 최대 유량 및 최소 컷 정리에 의해 최대 유량과 같으므로 이분 매칭을 통해 없애야 하는 간선의 최소수를 구할 수 있다.
- 투표수 - 최대 유량이 투표가 반영되는 시청자의 최댓값이다.
4. 실수한 점, 개선할 점
- 맨 처음에 최대 유량 및 최소 컷 정리를 생각해서 dfs로는 못 풀 것이라고 생각했는데 dfs로 풀 수 있어서 dfs로 바꾸었다.
- 두 동물이 같은지를 비교하는 것은 .compare()매소드를 이용하면 된다.
- 내가 짠 코드를 보면 고양이를 좋아하는 사람과 개를 좋아하는 사람끼리 충돌이 일어나서 결국 간선을 두 번 만들어주게 된다. 이를 간선을 한 번 만드는 것으로 줄일 수 있고 이러면 소요 시간이 줄어든다. 이렇게 코딩한 코드가 있어서 아래에 링크하였다.
<코드>
#include <iostream>
#include <string.h>
#include <vector>
#include <queue>
using namespace std;
const int INF = 987654321;
const int MAX_V = 501;
//방향 그래프
vector<int> adj[MAX_V];
//테스트 케이스의 수 T
int T;
//고양이의 수 c, 개의 수 d, 투표한 시청자의 수 v
int c, d, v;
//각 사람의 평가(좋아하는 것, 싫어하는 것)
string eval[MAX_V][2];
//dfs용
int Occupy[MAX_V];
bool done[MAX_V];
void input() {
for(int i=0; i<MAX_V; i++) {
adj[i].clear();
eval[i][0].clear();
eval[i][1].clear();
}
memset(Occupy, 0, sizeof Occupy);
cin >> c >> d >> v;
for(int i=1; i<=v; i++)
cin >> eval[i][0] >> eval[i][1];
//의견이 충돌하는 것끼리 연결시켜주기
for(int i=1; i<v; i++) {
for(int j=i+1; j<=v; j++) {
if(!eval[i][0].compare(eval[j][1]) || !eval[i][1].compare(eval[j][0])) {
if(eval[i][0][0] == 'C')
adj[i].push_back(j);
else
adj[j].push_back(i);
}
}
}
}
bool dfs(int Node) {
for(auto Next : adj[Node]) {
if(done[Next])
continue;
done[Next] = true;
if(!Occupy[Next] || dfs(Occupy[Next])) {
Occupy[Next] = Node;
return true;
}
}
return false;
}
int main() {
ios_base::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin >> T;
while(T--) {
input();
/*
for(int i=0; i<=v; i++)
for(auto x : adj[i])
cout << i << ' ' << x->next << '\n';
*/
//이분 매칭
int total_flow = 0;
for(int i=1; i<=v; i++) {
memset(done, false, sizeof done);
if(dfs(i))
total_flow++;
}
//충돌이 일어나는 개수만큼 사용자의 의견을 무시하면 된다.
cout << v-total_flow << '\n';
}
return 0;
}
<참고한 코드(풀어야 볼 수 있습니다.)>
https://www.acmicpc.net/source/157033
로그인
www.acmicpc.net
※현재 고등학교 등교 중인 학생입니다. 이제 알고리즘을 본격적으로 공부하기 시작해서 아직 초보입니다. 혹시 제가 잘못 알고 있는 점이나 더 좋은 풀이 방법이 있어 댓글에 남겨주시면 감사히 하나하나 열심히 읽어보겠습니다. 좋아요, 단순한 댓글 한마디라도 저에겐 큰 힘이 됩니다! 감사합니다.
'알고리즘 > 백준' 카테고리의 다른 글
[C++] 11377번 열혈강호 3 - 최대 유량, 이분 매칭 (0) | 2022.11.03 |
---|---|
[C++] 10254번 고속도로 - 기하학, 볼록 껍질, 회전하는 캘리퍼스 (0) | 2022.11.02 |
[C++] 3640번 제독 - 최대 유량, 최소 비용 최대 유량 (0) | 2022.11.01 |
[C++] 1006번 습격자 초라기 - DP (0) | 2022.10.31 |
[C++] 19124번 Binomial Coefficient - 수학, 정수론 (0) | 2022.10.28 |
Comments