백준 코딩
백준:9466번 팀 프로젝트
까르르꿍꿍
2021. 10. 6. 19:23
https://www.acmicpc.net/problem/9466
9466번: 텀 프로젝트
이번 가을학기에 '문제 해결' 강의를 신청한 학생들은 텀 프로젝트를 수행해야 한다. 프로젝트 팀원 수에는 제한이 없다. 심지어 모든 학생들이 동일한 팀의 팀원인 경우와 같이 한 팀만 있을
www.acmicpc.net
이번 문제를 풀때 생각했던 것은 사이클과 DFS 이다. 일단 사이클은 이미 방문한 지점을 다시 들르면 사이클이라 정의했었다.
나는 접근을 맨 처음 들어간 값과 마지막이 가리키는 값이 일치하면 사이클로 1개씩 추가하고 배열들을 매번 초기화 해줬다. 밑에 코드를 보자
#include<iostream>
#include<vector>
#include<algorithm>
#include<cmath>
#include<set>
#include<cstring>
#include<stack>
#include<queue>
#define PI 3.1415926535
using namespace std;
bool check1[100001] = { 0 };
bool check2[1001] = { 0 };
vector<int> d[100001];
int cnt = 0;
void dfstack(int start,int p) { //스택을 이용한 dfs
memset(check1, false, (p + 1) * sizeof(bool));
stack<int> s;
int current = start;
s.push(current);
check1[current] = true;
while (!s.empty()) {
current = s.top();
s.pop();
int temp = d[current].size();
for (int i = 0; i < temp; i++) {
int next = d[current][i];
if (next == start) //첫 시작과 같으면 사이클로 인정
cycle = true;
if (check1[next] == false) {
check1[next] = true;
s.push(current);
s.push(next);
break;
}
}
}
}
int main()
{
int n, p,t;
vector<int> a;
cin >> t;
while (t--) {
int cnt = 0;
cin >> n;
a.clear();
a.push_back(0);
for (int i = 1; i <= n; i++) {
cin >> p;
a.push_back(p);
}
for (int i = 1; i <= n; i++) {
d[i].clear(); /계속 초기화
d[i].push_back(a[i]);
}
for (int i = 1; i <= n; i++) {
dfstack(i,n);
if (cycle == false) //사이클이 아니면 값추가
cnt++;
cycle = false;
}
cout << cnt << '\n'; //정답
}
return 0;
}
이 코드를 보면 답은 일치하게 나온다. 그렇지만 배열을 반복문을 돌릴때마다 초기화를 하기 때문에 시간초과가 걸린다.
결국 이 방법은 효율적이지 못한다. 이렇게 매번 초기화하면서 값을 추가한 이유는 사이클이 겹치면서 구분지어줄 방법이 없었기 때문이다.
그래서 사이클이 만들어질때 시작한 값을 미리 지정을 하면 사이클내에서도 중복된 것을 뺼 수있다.
밑에 코드를 보자
#include<iostream>
#include<vector>
#include<algorithm>
#include<cmath>
#include<set>
#include<cstring>
#include<stack>
using namespace std;
int check1[100001] = { 0 };
bool check2[1001] = { 0 };
vector<int> d[100001];
int cnt = 0;
int s[100001] = { 0 };
int ans = 0;
int dfs(int a, int cnt, int start) {
if (check1[a] != 0) { //이미 방문 되어있고
if (start != s[a]) { //시작했던 값이 다르면 즉 이미 1번으로 3번을 방문했었다면 두번 방문 방지
return 0;
}
return cnt - check1[a]; //아니면 사이클까지 걸린 방문수에서 사이클 시작한 방문수 뻄
}
check1[a] = cnt; //몇번째 방문인지 값 넣기
s[a] = start; //몇번 정점으로 시작했는지
for (int i = 0; i < d[a].size(); i++) {
int next = d[a][i];
if (check1[i] == 0)
return dfs(next, cnt + 1, start);
}
}
int main()
{
int n, p,t;
vector<int> a;
cin >> t;
while (t--) {
int cnt = 0;
cin >> n;
a.clear();
a.push_back(0);
for (int i = 1; i <= n; i++) {
cin >> p;
a.push_back(p);
}
for (int i = 1; i <= n; i++) {
d[i].clear();
d[i].push_back(a[i]);
s[i] = 0;
check1[i] = 0;
}
for (int i = 1; i <= n; i++) {
if(check1[i]==0)
ans+=dfs(i,1,i);
}
cout << n-ans << '\n';
ans = 0;
}
return 0;
}
이 코드를 보면 굳이 배열을 초기화를 굳이 안해도 이미 세어버린 사이클은 시작정점으로 구분지으면서 중복안되게 한다.