본문 바로가기

백준

C++[백준]1658번 돼지 잡기

https://www.acmicpc.net/problem/1658

 

1658번: 돼지 잡기

첫째 줄에 돼지 우리 숫자를 나타내는 M(1≤M≤1,000)과 손님들의 숫자를 나타내는 N(1≤N≤100)이 공백으로 구분되어 주어진다. 돼지우리는 1번부터 M번까지의 숫자로 매겨져 있고 손님 역시 1번부

www.acmicpc.net

문제가 좀 조건대로 하면... 안풀리는 것인데 풀리니 이상하다 생각을 하고 푼 문제. (솔루션을 봤음..)

 

처음에 조건 3.을 제대로 보지 않아서  소스 - 돼지우리 - 사람 - 싱크 이렇게 노드를 연결하여 풀 수 있을 것이라 생각했다.

 

그런데 문을 열었을 때, 남은 돼지를 적절히 재분배 할 수 있다는 조건이 상당히 까다로워졌다.

 

도저히 문제를 해결못해서 솔루션을 봤다.

 

원래라면 디닉으로 풀리지 않는 시간 복잡도를 가졌다. O( (M*N)^2*(M*M)) 그래서 안될거라고 생각하고있었는데, 다른 사람들이 이렇게 풀면 풀린다고 한다...

아무래도 채점 데이터가 약한 것 같다.

 

솔루션


 

 

해법

1. 소스를 0으로 두고 첫째날 돼지우리를 1~M 까지 둔다.

2. X날 -> 다음 날 넘어가는 돼지 우리를  연결한다. (1~M) -> (M+1 ~ 2M, 일대일 대응)

3. 각 날짜에 오는 손님과 돼지 우리를 연결한다. (N*M+ 손님이 오는 날짜) 

    손님이 문을 여는 돼지 우리를 연결해 준다. (돼지우리를 1, 2를 열였으면, 1->2, 2->1 각각 간선을 연결한다.)

4. 각 손님과 싱크를 연결해 준다.

 

그림으로 표현하면 다음과 같다.

 

코드


#define INF 987654321
#include <iostream>
#include <vector>
#include <queue>
using namespace std;

struct edge {
	int next, capacity, flow, inv;
};

int N, M;
vector<edge> V[100200];


int level[100200];
bool bfs(int s, int t) {
	fill(level, level + M * N + N + 2, -1);

	level[s] = 0;
	queue<int> Q;
	Q.push(s);

	while (!Q.empty()) {
		int cur = Q.front();
		Q.pop();

		for (auto& e : V[cur]) {
			if (level[e.next] == -1 && e.capacity > e.flow) {
				level[e.next] = level[cur] + 1;
				Q.push(e.next);
			}
		}
	}
	return level[t] != -1;
}

int start[100200];
int dfs(int cur, int t, int f) {
	if (cur == t) return f;

	for (int& i = start[cur]; i < V[cur].size();i++) {
		auto& e = V[cur][i];
		if (level[e.next] == level[cur] + 1 && e.capacity > e.flow) {
			int df = dfs(e.next, t, min(f, e.capacity - e.flow));
			if (df) {
				e.flow += df;
				V[e.next][e.inv].flow -= df;
				return df;
			}
		}
	}
	return 0;
}


int main()
{
	ios_base::sync_with_stdio(false);
	cin.tie(NULL); cout.tie(NULL);

	cin >> M >> N;

	for (int i = 1; i <= M; i++) {
		int value;
		cin >> value;
		V[0].push_back({ i,value,0,(int)V[i].size() });
		V[i].push_back({ 0,0,0,(int)V[0].size() - 1 });
	}

	for (int i = 2; i <= N; i++) {
		int cur = i * M;
		
		for (int  j= cur; j >(cur-M); j--) {
			V[j].push_back({ j - M,0,0,(int)V[j - M].size() });
			V[j - M].push_back({ j,INF,0,(int)V[j].size() - 1 });
		}
	}

	for (int i = 1; i <= N; i++) {
		vector<int> input;
		int cur = (i-1) * M;
		int tc;
		cin >> tc;
		while (tc--) {
			int value;
			cin >> value;
			input.push_back(value);
			//돼지 우리 - 사람과의 간선
			V[cur + value].push_back({ M * N + i,INF,0,(int)V[M * N + i].size() });
			V[M * N + i].push_back({ cur + value,0,0,(int)V[cur + value].size() - 1 });
		}

		int cap;
		cin >> cap;
		//사람 - 싱크
		V[M * N + i].push_back({ M * N + N + 1,cap,0,(int)V[M * N + N + 1].size() });
		V[M * N + N + 1].push_back({ M * N + i,0,0,(int)V[M * N + i].size() - 1 });

		if (i == N) break;
		for (int j = 0; j < input.size(); j++) { //돼지 우리 -> 다음 날 돼지 우리
			int node1 = cur + input[j];
			for (int k = 0; k < input.size(); k++) {
				if (j == k) continue;
				int node2 = i * M + input[k];

				V[node1].push_back({ node2,INF,0,(int)V[node2].size() });
				V[node2].push_back({ node1,0,0,(int)V[node1].size() - 1 });
			}			
		}
	}


	//총 M + 2N개의 노드.
	
	int maxflow = 0;
	while (bfs(0, M *N+N+1)) {
		fill(start, start + M * N + N + 2, 0);
		while (int df = dfs(0, M * N+N + 1, INF)) {
			maxflow += df;
		}
	}
	cout << maxflow << '\n';
}

'백준' 카테고리의 다른 글

C++[백준]22342번 계산 로봇  (0) 2023.06.23
C++[백준]14846번 직사각형과 쿼리  (0) 2023.05.29
C++[백준]1996번 지뢰 찾기  (0) 2023.05.29
C++[백준] 1761번 정점들의 거리  (2) 2023.05.21
C++[백준] 25402번 트리와 쿼리  (1) 2023.05.20