프로그래밍/알고리즘

Softeer-연습문제-[21년 재직자 대회 예선] 로드 밸런서 트래픽 예측(C++)

Sechan Oh 2023. 5. 30. 12:02

문제 링크 :

https://softeer.ai/practice/info.do?idx=1&eid=629

 

문제 개요 : 

 

시간초과 코드 (브루트포스)

#include<iostream>
#include<vector>

using namespace std;

void req(unsigned long node, vector<vector<unsigned long>> tree,
vector<unsigned long> &pointer, vector<unsigned long> &answer);

int main(int argc, char** argv)
{
	unsigned long N, K;
	scanf("%lu %lu", &N, &K);

	vector<unsigned long> answer(N, 0);
	vector<unsigned long> pointer(N, 1);
	vector<vector<unsigned long>> tree; // len, data

	for (int i=0; i<N; i++){
		unsigned long n;
		scanf("%lu", &n);
		vector<unsigned long> tmp;
		for(int j=0; j<n; j++){
			unsigned long n2;
			scanf("%lu", &n2);
			tmp.push_back(--n2);
		}
		tree.push_back(tmp);
		tree[i].insert(tree[i].begin(), n+1);
	}

	for(unsigned long i=0; i<K; i++){
		req(0, tree, pointer, answer);
	}

	for(int i=0; i<N; i++){
		printf("%lu ", answer[i]);
	}

	return 0;
}

void req(unsigned long node, vector<vector<unsigned long>> tree,
vector<unsigned long> &pointer, vector<unsigned long> &answer){
	// ans++, ptr++, 재귀
	answer[node]++;
	if (tree[node][0]==1) return; // leaf
	unsigned long nxt_node = tree[node][pointer[node]];
	req(nxt_node, tree, pointer, answer);
	if (tree[node][0]==++pointer[node]) pointer[node]=1;
}

 

시간초과 코드(batch 처리)

#include<iostream>
#include<vector>
#include<queue>

using namespace std;

int main(int argc, char** argv)
{
	// N, K 입력받기
	unsigned long N, K;
	scanf("%lu %lu", &N, &K);

	vector<unsigned long> answer(N, 0);
	vector<unsigned long> tmp_req(N, 0);
	vector<unsigned long> pointer(N, 0);
	vector<vector<unsigned long>> tree;
	queue<unsigned long> q;

	// data 입력받기
	for (unsigned long i=0; i<N; i++){
		unsigned long n;
		scanf("%lu", &n);
		vector<unsigned long> tmp;
		for(int j=0; j<n; j++){
			unsigned long n2;
			scanf("%lu", &n2);
            // tree에 넣을 자식노드
			tmp.push_back(--n2);
		}
		tree.push_back(tmp);
	}

	// 초기값 설정
	tmp_req[0] = K;
	q.push(0);

	// main
	while (q.empty()==false){
		// 다음노드 호출
		unsigned long node = q.front();
		q.pop();
		// 노드의 크기 가져오기
		unsigned long n_sz = tree[node].size();
		// 이 노드의 tmp_req 값이 있고 leaf 노드가 아닐 경우
		if (tmp_req[node]>0 && n_sz>0){
			// 노드의 req값 가져오기
			unsigned long req = tmp_req[node];
			// 최대 point 구하기
			unsigned long max_point = pointer[node] + req % n_sz;
			// 사이클 수 구하기
			unsigned long cycle = req/n_sz;
			// 사이클이 있을 경우
			if (cycle>0){
				// 자식 노드를 돌면서
				for (unsigned long i=0;i<n_sz;i++){
					// 자식노드 찾기
					unsigned long child = tree[node][i];
					// 자식노드의 tmp_req 추가
					tmp_req[child] += cycle;
					// 자식노드를 queue에 추가
					q.push(child);
				}
				// pointer 움직이면서
				for (unsigned long i=pointer[node];i<max_point;i++){
					// 자식노드 찾기
					unsigned long child = tree[node][i%n_sz];
					// 자식노드의 tmp_req 증가
					tmp_req[child]++;
				}
			}
			else{
				// pointer 움직이면서
				for (unsigned long i=pointer[node];i<max_point;i++){
					// 자식노드 찾기
					unsigned long child = tree[node][i%n_sz];
					// 자식노드의 tmp_req 증가
					tmp_req[child]++;
					// 자식노드를 queue에 추가
					q.push(child);
				}
			}
			// 포인터 옮기기
			pointer[node] = max_point % n_sz;
		}
		// tmp_req값을 answer로 옮기기
		answer[node] += tmp_req[node];
		tmp_req[node] = 0;
	}

	// 정답 출력
	for(unsigned long i=0;i<N;i++){
		printf("%lu ",answer[i]);
	}

	return 0;
}

 

정답 코드 (위상정렬 알고리즘)

#include<iostream>
#include<vector>
#include<queue>

using namespace std;

// 위상정렬 알고리즘
vector<unsigned long> topological_sort(
	vector<vector<unsigned long>> tree,
	vector<unsigned long> degree,
	const unsigned long N);
// req 계산 알고리즘
vector<unsigned long> calc_req(
	const vector<unsigned long> seq,
	const vector<vector<unsigned long>> tree,
	const unsigned long N,
	const unsigned long K);

int main(int argc, char** argv)
{
	// N, K 입력받기
	unsigned long N, K;
	scanf("%lu %lu", &N, &K);

	// 사용할 자료구조 선언
	vector<vector<unsigned long>> tree;
	vector<unsigned long> degree(N, 0);

	// data 입력받기
	for (unsigned long i=0; i<N; i++){
		// i 노드의 자녀 수 입력
		unsigned long n;
		scanf("%lu", &n);
		vector<unsigned long> tmp;
		for(int j=0; j<n; j++){
			// 자녀노드 입력
			unsigned long n2;
			scanf("%lu", &n2);
            // tree에 넣을 자녀노드를 tmp에 저장
			tmp.push_back(--n2);
			// 자녀노드의 위상 수 세기
			degree[n2]++;
		}
		// tree 갱신
		tree.push_back(tmp);
	}

	// req 계산
	vector<unsigned long> ans = calc_req(
        topological_sort(tree,degree,N),
        tree, N, K);

	// 정답 출력
	for(unsigned long i=0;i<N;i++){
		printf("%lu ", ans[i]);
	}

	return 0;
}

// 위상정렬 알고리즘
vector<unsigned long> topological_sort(
	const vector<vector<unsigned long>> tree,
	vector<unsigned long> degree,
	const unsigned long N){

	// 사용할 자료구조 선언
	queue<unsigned long> q;
	vector<unsigned long> answer;

	// 초기값 설정
	q.push(0);

	while(!q.empty()){
		// 다음 노드 불러오기
		unsigned long node = q.front();
		q.pop();
		// 노드를 q의 순서대로 정렬
		answer.push_back(node);
		// 자녀노드를 돌면서
		for (unsigned long j=0;j<tree[node].size();j++){
			unsigned long child = tree[node][j];
			// 자녀노드의 차수 갑소
			degree[child]--;
			// 위상 수가 0이 되면 q에 추가
			if (degree[child]==0) q.push(child);
		}
	}
	return answer;
}

vector<unsigned long> calc_req(
	const vector<unsigned long> seq,
	const vector<vector<unsigned long>> tree,
	const unsigned long N,
	const unsigned long K){

	// 사용할 자료구조 선언
	vector<unsigned long> answer(N, 0);

	// 초기값 설정
	answer[0]=K;

	// 위상정렬 된 순서대로 노드를 돌면서
	for (unsigned long i=0;i<seq.size();i++){
		// 다음 순서 노드 불러오기
		unsigned long node = seq[i];
		// req값 가져오기
		unsigned long req = answer[node];
		// 노드의 크기 가져오기
		unsigned long n_sz = tree[node].size();
		// leaf 노드가 아닐 경우
		if (n_sz>0){
			// 사이클 수 구하기
			unsigned long cycle = req/n_sz;
			// 사이클이 있을 경우
			if (cycle>0){
				// 자식 노드를 돌면서
				for (unsigned long i=0;i<n_sz;i++){
					// 자식노드 찾기
					unsigned long child = tree[node][i];
					// 자식노드의 answer에 cycle 추가
					answer[child] += cycle;
				}
			}
			// cycle 돌고 남은 req를 자식노드에게 하나씩 분배
			for (unsigned long i=0; i<(req % n_sz); i++){
				// 자식노드 찾기
				unsigned long child = tree[node][i];
				// 자식노드의 answer 증가
				answer[child]++;
			}
		}
	}

	return answer;
}

 

문제 해결 방법

1. 데이터를 입력받으면서 tree(트리구조)와 degree(위상수)를 만들어 둔다.

2. 위상정렬 알고리즘으로 노드를 정렬한다.

3. 정렬된 순서대로 노드를 돌면서 요청 수를 계산한다.