2021. 2. 24. 19:04ㆍ알고리즘/알고리즘 문제 [hard]
1. 문제
N명의 선수를 승부 조작에 참여시켰다.
결승 리그는 여러번의 1:1 경기이다
무승부 없이 항상 승부가 나며
모든 경기가 끝난 후 승수가 가장 많은 선수가 우승한다.
만약 가장 승수가 많은 선수가 둘 이상 있을 겨우 공동 우승을 하게 된다
대부분 우승하지 못할 것으로 여겨지는 X 를 단독 우승하도록해서 이득을 취하려고한다
의심을 피하기위해 가능한 X가 적은 승수로 우승하도록 하고싶다
현재 각 선수의 승수와 남아 있는 경기 목록이 주어질 때.
승부를 적절히 조작해 X가 우승하도록 할 수 있는지 여부를 계산하고,
우승할 수 있다면 필요한 X의 최소 승수는 얼마인지 계산하는 프로그램을 작성하여라.
이때 단독 우승이 불가능할 경우 -1을 출력한다.
그리고 모든 선수가 같은 수의 경기를 치르도록 보장되어 있지 않는다.
2. 책의 풀이
유량 네트워크 만들기
0번 선수가 총 w승으로 단독 우승하기 위해서는 다음 두 가지 조건이 모두 성립해야한다.
1. 0번 선수가 남은 경기를 전부 승리했다고 가정했을 때 w승 이상을 할 수 있어야한다.
2. 남은 경기의 승패가 적절히 결정되어 다른 선수들은 모두 w -1 승 이하로 리그를 마칠 수 있어야한다.
이 두 조건을 만족해야하는 것과 이 문제가 자원 배분 문제임을 알아채 적절히 그래프를 만들고,
포드-풀커슨 알고리즘을 사용하면 문제를 해결할 수 있다.
3. 2번 조건 만족시키기
2번 조건인
0번선수를 제외하고 모든 선수가 w-1승이하가 되어야한다는 것
을 만족시키기 이전에 그래프를 표현하는 방법부터 정해야한다.
이 문제는 '승'이라는 자원을 배분하는 문제이다.
그러므로 '경기'들 정점으로 보내는 1승을 보내는 '소스'와
각 선수들이 받은 승들을 받는 '싱크' 를 따로 생성해야한다.
또한 이 자원의 총 개수는 '남은 경기수 + 현재 승수의 합' 이 되어야한다.
하지만 현재 승수의 합은 고정적이므로 '남은 경기수' 만 생각하면 된다.
여기서 0을제외한 모든 선수가 w-1승 이하가 되어야하므로
capacity[i + m + 2][1] = (w-1 - win[i])
(0: 소스 , 1: 싱크, win[i]: i번 선수의 현재 승수, 0 + m+2: 그래프상에서 0 번 선수의 인덱스 )
위 식이 선수에서 싱크로가는 용량이 된다면 항상 w-1이하 ( 0번은 w) 가 된다.
그러므로 만약
소스 - 1 - 경기
경기 -1- 선수a
- 선수b
선수- c - 싱크
같은 형태의 그래프에 포드-풀커슨 알고리즘을 적용하고 결과값이 m이 된다면
이는 모든 선수의 승수를 w-1이하로 만들 수 있다는게 된다.
4. 1번 조건 만족시키기
1번 조건은 0번이 w가 될 수 있는지를 확인하면 된다.
하지만 이는 bfs탐색을 항상 0 번부터 채워 넣으면
입력단에서 0을 포함한 경기수 x를 센다음
win[0] <= w <= win[0] + x
의 범위만 조사하면 자연스럽게 만족된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
|
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <utility>
using namespace std;
const int INF = 987654321;
int V, n, m, totalCount, xmatch;
vector<vector<int> > capacity;
vector<int> currentWin;
vector<pair<int, int> > match;
vector<vector<int> > flow;
int networkFlow(int src, int sink)
{
while(true)
{
queue<int> q;
vector<int> parent = vector<int>(V, -1);
parent[src] = src;
q.push(src);
while(!q.empty() && parent[sink] == -1)
{
int here = q.front(); q.pop();
for(int there = 0; there < V; there++)
{
if(capacity[here][there] - flow[here][there] > 0 && parent[there] == -1)
{
q.push(there);
parent[there] = here;
}
}
}
if(parent[sink] == -1) break;
int cost = INF;
for(int i = sink; i != src; i = parent[i])
cost = min(cost, capacity[parent[i]][i] - flow[parent[i]][i]);
for(int i = sink; i != src; i = parent[i])
{
flow[i][parent[i]] -= cost;
flow[parent[i]][i] += cost;
}
totalCount += cost;
}
return totalCount;
}
bool isCanWin(int w)
{
for(int i = 0 ; i < n ; i++){
capacity[i + m + 2][1] = ((i == 0 ? w: w-1) - currentWin[i]);
if(capacity[i+m+2][1] < 0) return false;
}
return (networkFlow(0, 1) == m);
}
int solve()
{
int ret = -1;
capacity = vector<vector<int> > (V , vector<int>(V, 0));
flow = vector<vector<int> >(V, vector<int>(V, 0));
for(int i = 0; i < m ; i++)
{
capacity[0][i+2] = 1;
capacity[i+2][m + 2 + match[i].first] = 1;
capacity[i+2][m + 2 + match[i].second] = 1;
}
for(int i = currentWin[0]; i <= currentWin[0] + xmatch ; i++)
if(isCanWin(i))
return i;
return ret;
}
int main()
{
int C;
cin>>C;
while(C--)
{
cin>>n>>m;
currentWin = vector<int>(n, 0);
match = vector<pair<int, int> > (m);
xmatch = 0;
totalCount = 0;
V = n + m + 2;
for(int i = 0; i < n; i++)
cin>>currentWin[i];
for(int i = 0; i < m; i++)
{
int a, b;
cin>>a>>b;
match[i] = make_pair(a, b);
if(a == 0 || b == 0)
xmatch++;
}
cout<<solve()<<"\n";
}
}
|
cs |
www.algospot.com/judge/problem/read/MATCHFIX
'알고리즘 > 알고리즘 문제 [hard]' 카테고리의 다른 글
[그래프][시도x] NTHLON 철인 N종 경기 (0) | 2021.02.24 |
---|---|
[그래프] [시도x] TPATH 여행 경로 정하기 (0) | 2021.02.23 |
[그래프] [시도 x] PROMISES 선거 공약 (0) | 2021.02.22 |
[그래프][시도x] CHILDRENDAY 어린이날 (0) | 2021.02.16 |
[그래프] [2-SAT] [시도 x] MEETINGROOM 회의실 배정 (0) | 2021.02.15 |