Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tree #25

Merged
merged 9 commits into from
Nov 24, 2021
Merged

Tree #25

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions 11월16일-트리/14503.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#include <iostream>
#include <vector>

using namespace std;

int cntCleanRobot(int r, int c, int d, vector<vector<int>>& board) {
//상, 우, 하, 좌
int dr[4] = { -1, 0, 1, 0 }; //행
int dc[4] = { 0, 1, 0, -1 }; //열

int step = 0, ans = 0; //회전, ans
while (true) {//계속 진행
if (board[r][c] == 0) {//빈칸이면
board[r][c] = 2; //청소
ans++; //+1
}

if (step == 4) {//4방향
if (board[r - dr[d]][c - dc[d]] == 1)//벽 마주치면
return ans; //청소 갯수 리턴
r -= dr[d];//행 움직임
c -= dc[d];//열 움직임
step = 0;//초기화
}
else {
d = (d + 3) % 4; //왼쪽으로
if (board[r + dr[d]][c + dc[d]]) {//이동
step++;//step+1
continue;//계속하기
}
r += dr[d];//행 움직임
c += dc[d];//열움직임
step = 0;//초기화
}
}
}

/**
* board 정보 -> 0: 빈 칸, 1: 벽, 2: 청소한 공간
* step: 회전 카운트. 4가 되면 한 바퀴 돌아 다시 제자리로 돌아왔음을 의미
*
* 항상 첫 행, 마지막 행, 첫 열, 마지막 열은 벽이라고 문제에서 주어졌으므로 범위 검사를 할 필요가 없음
*/

int main() {
int n, m, r, c, d; //변수

//입력
cin >> n >> m >> r >> c >> d;
vector<vector<int>> board(n, vector<int>(m, 0)); //저장
for (int i = 0; i < n; i++) { //n개의
for (int j = 0; j < m; j++) {//m열에 대하여
cin >> board[i][j]; //정보 입력
}
}

//연산 & 출력
cout << cntCleanRobot(r, c, d, board);
return 0; //종료
}
40 changes: 40 additions & 0 deletions 11월16일-트리/14675.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#include <iostream>
#include <vector>

using namespace std;

//단절점, 단절선 파악 함수
string isTrue(int t, int k, vector<vector<int>>& tree) {
if (t == 2) //2면
return "yes"; //yes
if (tree[k].size() >= 2) //2이상이면
return "yes";//yes
return "no";//그외 no
}

/**
* 부모 노드를 알 수 없기에 트리를 양방향 그래프로 저장
* 단절점: 트리는 모든 정점이 연결되어 있고, 싸이클이 존재하지 않기 때문에 연결된 간선이 2개 이상이면 단절점
* 단절선: 트리는 모든 정점이 연결되어 있고, 싸이클이 존재하지 않기 때문에 모든 간선이 단절선
*/

int main() {
ios_base::sync_with_stdio(false); //속도 향상
cin.tie(NULL); //속도 향상
int n, a, b, q, t, k; //변수

//입력
cin >> n;
vector<vector<int>> tree(n + 1, vector<int>(0)); //트리 벡터
for (int i = 0; i < n - 1; i++) { //정점 갯수 간 연결
cin >> a >> b; //a,b 연결
tree[a].push_back(b); // a-b
tree[b].push_back(a);//b-a
}
cin >> q; //질의
while (q--) { //질의 갯수만큼
cin >> t >> k; //t번째,k번째 간선에 대한 질의
cout << isTrue(t, k, tree) << '\n'; //질의
}
return 0; //종류
}
51 changes: 51 additions & 0 deletions 11월16일-트리/15681.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#include <iostream>
#include <vector>

using namespace std;

vector<int> subtree_cnt; //서브트리

int treeDp(int cur, vector<vector<int>>& graph) {
if (subtree_cnt[cur] != -1) //이미 탐색했던 상태
return subtree_cnt[cur]; //리턴

//서브트리에 속한 정점의 수를 세고, 저장
subtree_cnt[cur] = 0;
int node = 1;//초기 갯수
for (int i = 0; i < graph[cur].size(); i++) //그래프 사이즈만큼 탐색
node += treeDp(graph[cur][i], graph); //저장
return subtree_cnt[cur] = node;//리턴
}

/**
* PPT 트리의 정점의 수 구하기 응용
*
* 1. 루트에서부터 dfs 탐색
* 2. 각 노드를 루트로 하는 서브트리의 정점 수를 재귀적으로 계산
*/
int main() {
ios::sync_with_stdio(false); //속도 향상
cin.tie(NULL); //속도 향상

int n, r, q, u, v, input; //변수

//입력
cin >> n >> r >> q;
vector<vector<int>> graph(n + 1, vector<int>(0)); //그래프 벡터
subtree_cnt.assign(n + 1, -1); //서브트리 초기화

while (--n) { //무방향 그래프
cin >> u >> v; //u-v
graph[u].push_back(v);//u-v
graph[v].push_back(u);//v-u
}

//연산
treeDp(r, graph);

//출력
while (q--) {
cin >> input; //쿼리
cout << subtree_cnt[input] << '\n'; //답 출력
}
}
55 changes: 55 additions & 0 deletions 11월16일-트리/1991.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#include <iostream>
#include <map>

using namespace std;

map<char, pair<char, char>> tree; //트리

//전위 순회
void preorder(char node) {
if (node == '.')//.이면
return;//종료
cout << node;//노드 출력
preorder(tree[node].first); //왼쪽
preorder(tree[node].second);//오른쪽
}

//중위 순회
void inorder(char node) {
if (node == '.')//.이면
return;//종료
inorder(tree[node].first);//왼쪽
cout << node;//노드 출력
inorder(tree[node].second);//오른쪽
}

//후위 순회
void postorder(char node) {
if (node == '.')//.이면
return;//종료
postorder(tree[node].first);//왼쪽
postorder(tree[node].second);//오른쪽
cout << node;//노드 출력
}

int main() {
int n; //갯수
char root, left, right; //루트, 왼,오

//입력
cin >> n;
while (n--) { //n개 입력
cin >> root >> left >> right;//입력
tree[root] = make_pair(left, right); //트리 생성
}

//연산 & 출력
preorder('A'); //전위 순회
cout << '\n'; //띄어쓰기

inorder('A');//중위 순회
cout << '\n';//띄어쓰기

postorder('A');//후위 순회
cout << '\n';//띄어쓰기
}
47 changes: 47 additions & 0 deletions 11월16일-트리/2011.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#include <iostream>
#include <vector>

using namespace std;
const int MOD = 1e6; //나머지 계산하여 정답 출력

int cntPassword(int n, string s) { //비밀번호 갯수 카운트
vector<int> dp(n + 1, 0); //dp
if (s[0] == '0') //0이면
return 0; //리턴

dp[0] = dp[1] = 1; //s[0]의 암호 해석 경우의 수 1로 초기화
for (int i = 2; i <= n; i++) { //2부터 n자리 수 탐색
if (s[i - 1] == '0' && (s[i - 2] < '1' || s[i - 2] > '2')) //암호해석 불가
return 0;//종료

if (s[i - 1] != '0') //0이 아니면
dp[i] += dp[i - 1]; //이전 dp 더하기
if (s[i - 2] == '1' || (s[i - 1] <= '6' && s[i - 2] == '2')) //이전 이전 dp에서
dp[i] += dp[i - 2]; //추가하기
dp[i] %= MOD; //나눠서 정답 출력
}
return dp[n];
}

/**
* dp[i] = i인덱스까지 암호 해석할 수 있는 경우의 수 저장
* => dp[i] = dp[i-1] + dp[i-2] (단, dp[i-1]과 dp[i-2]에서 이어서 암호 만들 수 있는 경우만)
*
* 1. '0' 혼자는 암호 해석 불가
* 2. 처음 시작이 '0'인 경우 주의
* 3. 현재 수가 '0'인데 앞의 수가 '1'이나 '2'가 아닌 경우 -> 암호 해석할 수 없음
* 4. 두 개의 수를 하나의 알파벳으로 고려할 때, 26이하의 수인지 잘 확인해야 함
*
* 본 풀이는 dp 배열 인덱스를 편하게 관리하기 위해 1번 인덱스부터 시작
*/

int main() {
string s;

//입력
cin >> s;

//연산 & 출력
cout << cntPassword(s.length(), s) << '\n';
return 0;//종료
}
83 changes: 83 additions & 0 deletions 11월16일-트리/2021카카오블라인드채용.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
#include <iostream>
#include <vector>

using namespace std;

vector<vector<int>> dp; //dp 벡터

vector<vector<int>> makeTree(int n, vector<vector<int>>& links) {
vector<vector<int>> tree(n, vector<int>(0)); //트리
for (int i = 0; i < links.size(); i++) //사이즈만큼
tree[links[i][0]].push_back(links[i][1]); //입력
return tree; //트리 리턴
}

int fillDp(vector<int>& sales, vector<vector<int>>& tree, int node, bool flag) {
if (dp[node][flag] != -1) //이미 탐색했던 상태
return dp[node][flag]; //리턴하기

/**
* 이 부분이 없다면?
* flag==true : 문제없음
* flag==false : attend_flag = false, diff 값이 한 번도 갱신되지 않음.
* -> dp[node][flag]에 INF32_MAX 값이 더해짐.
*
* flag==false 라도 말단 직원은 책임질 부하직원이 없기 떄문에 그냥 0을 리턴함
*/
if (tree[node].empty()) //말단직원
return dp[node][flag] = flag ? sales[node - 1] : 0; //리턴하기

dp[node][flag] = 0; //초기화
bool attend_flag = false; //부하직원 중 한 명이라도 참석했나?
int diff = INT32_MAX; //각 부하직원들의 참석/불참 매출 차이가 가장 적을 때
for (int i = 0; i < tree[node].size(); i++) { //노드 사이즈만큼 탐색
int next_node = tree[node][i]; //부하직원
int attend = fillDp(sales, tree, next_node, true); //참석하는 경우
int absent = fillDp(sales, tree, next_node, false); //불참하는 경우

dp[node][flag] += min(attend, absent); //더 작은 값 더하기
if (attend < absent) //부하직원이 한 명이라도 참석한 경우
attend_flag = true; //true
diff = min(diff, attend - absent); //모든 팀원이 불참했을 때를 대비
}
if (flag) //팀장 참석
return dp[node][flag] += sales[node - 1]; //더한후 리턴
if (!attend_flag) //팀장 + 부하직원 모두 불참
return dp[node][flag] += diff; //부하직원이 참석한 경우 중 매출 차이가 제일 적었던 경우를 더함
return dp[node][flag]; //팀장 불참 + 부하직원 중 1명 이상 참석
}

int solution(vector<int> sales, vector<vector<int>> links) {
int v = sales.size();//사이즈
dp.assign(v + 1, vector<int>(2, -1));//초기값
vector<vector<int>> tree = makeTree(v + 1, links); //트리 만들기

//CEO가 참석하는 경우, CEO가 참석하지 않는 경우
return min(fillDp(sales, tree, 1, true), fillDp(sales, tree, 1, false));
}

/**
* https://tech.kakao.com/2021/01/25/2021-kakao-recruitment-round-1/
*
* dp의 기본 아이디어는 이전의 상태 값을 기억하는 것
* 트리 dp? 트리에서의 이전 상태는 부모 정점의 상태라고 할 수 있음
*
* 1. 모든 직원들은 참석/불참 2가지 선택만 가능
* 2. 팀장이 참석했다면, 부하직원들은 참석/불참 상관없음
* 3. 팀장이 불참했다면, 부하직원들 중 1명 이상은 반드시 참석해야 함
* 4. 루트에서부터 dfs 탐색을 하며 매출하락을 최소화하기
*/
int main() {
vector<int> sales = { 14, 17, 15, 18, 19, 14, 13, 16, 28, 17 }; //sales 정보
vector<vector<int>> links = { {10, 8}, //link정보
{1, 9}, //link정보
{9, 7}, //link정보
{5, 4}, //link정보
{1, 5}, //link정보
{5, 10}, //link정보
{10, 6}, //link정보
{1, 3}, //link정보
{10, 2} }; //link정보
int ans = solution(sales, links); //정답 출력
cout << ans;//출력
}
Loading