-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
5c14270
commit 037a9e8
Showing
4 changed files
with
325 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
// | ||
// Created by user on 2021-11-16. | ||
// | ||
|
||
#include <iostream> | ||
#include <vector> | ||
|
||
using namespace std; | ||
|
||
int n, m, ans = 65; // n: 세로 개수, m: 가로 개수 | ||
vector<vector<int>> board; // 사무실 상태 표시할 보드 | ||
|
||
//cctv 범위 표시(체크) | ||
void ray(int row, int col, int dir) { | ||
int dr[4] = {-1, 0, 1, 0}; | ||
int dc[4] = {0, 1, 0, -1}; | ||
// (r, c): (-1, 0): 상 | ||
// (0, 1): 우 | ||
// (1, 0): 하 | ||
// (0, -1): 좌 | ||
while (row >= 0 && row < n && col >= 0 && col < m && board[row][col] != 6) { //dir 방향으로 뻗어나가며 cctv 표시 | ||
if (board[row][col] == 0) //다른 cctv를 지우지 않기 위해 빈 공간일 때만 표시 | ||
board[row][col] = 7; // 범위를 벗어나거나 벽을 만나면 visited 표시(여기선 7) | ||
row += dr[dir]; // row 갱신 | ||
col += dc[dir]; // col 갱신 | ||
} | ||
} | ||
|
||
//cctv 방향 지정 | ||
void install(int cctv, int row, int col, int dir) { | ||
// dir == 1일 경우 예시(블로그 참고) | ||
if (cctv >= 1) //1, 2, 3, 4, 5번 cctv | ||
ray(row, col, dir); // 위 | ||
if (cctv >= 2 && cctv != 3) //2, 4, 5번 cctv | ||
ray(row, col, (dir + 2) % 4); // 위 아래 | ||
if (cctv >= 3) //3, 4, 5번 cctv | ||
ray(row, col, (dir + 1) % 4); // 우측 추가 | ||
if (cctv == 5) //5번 cctv | ||
ray(row, col, (dir + 3) % 4); // 좌측 추가 | ||
} | ||
|
||
//사각지대 계산 | ||
int blindSpot() { | ||
int cnt = 0; | ||
for (int i = 0; i < n; i++) { // 모든 cctv 배치 | ||
for (int j = 0; j < m; j++) { | ||
if (!board[i][j]) // board[i][j] == 0인 경우 | ||
cnt++; // cnt 증가 | ||
} | ||
} | ||
return cnt; // 사각지대 리턴 | ||
} | ||
|
||
void backtracking(int idx) { // 백트래킹 함수 | ||
if (idx == n * m) { //기저 조건 : 사무실의 모든 위치 확인 | ||
ans = min(ans, blindSpot()); //사각지대 계산 후 최솟값 갱신 | ||
return; // 함수 종료 | ||
} | ||
// idx != n * m | ||
int row = idx / m; // row값 초기화 | ||
int col = idx % m; // col값 초기화 | ||
int cur = board[row][col]; // 이번에 설치할 cctv | ||
if (cur == 0 || cur == 6 || cur == 7) //cctv가 없는 곳 | ||
return backtracking(idx + 1); // 재귀 호출 | ||
|
||
vector<vector<int>> save = board; //unvisited 처리용 board 상태 저장 | ||
for (int i = 0; i < 4; i++) { //4개의 방향에 대해 cctv 설치 | ||
install(cur, row, col, i); // visited 처리 | ||
backtracking(idx + 1); // 재귀 호출 | ||
board = save; // unvisited 처리 | ||
|
||
//2번 cctv의 방향 종류는 2개, 5번 cctv의 방향 종류는 1개 | ||
if ((cur == 2 && i == 1) || (cur == 5 && i == 0)) | ||
break; // 체크 후 backtracking 종료 | ||
} | ||
} | ||
|
||
/** | ||
* cctv가 설치될 수 있는 모든 경우를 고려하는 완전탐색 문제 | ||
* | ||
* 1. 각 cctv에 대해 가능한 모든 방향을 고려하여 설치 | ||
* 1, 3, 4번 cctv : 4방향 | ||
* 2번 cctv : 2방향 | ||
* 5번 cctv : 1방향 | ||
* 2. install 함수에서 각 cctv의 빛이 뻗어나갈 방향을 잡음 | ||
* 3. ray 함수에서 cctv의 감시 가능 범위 표시 | ||
* 4. 모든 위치를 확인했으면 blindSpot 함수를 통해 사각지대 계산 | ||
* | ||
* 풀이 : https://myunji.tistory.com/411 | ||
* 해당 코드는 위 링크의 코드를 리팩토링한 코드입니다. | ||
* 말로는 풀이를 설명하기 어려우니 링크를 꼭 확인해주세요! | ||
*/ | ||
int main() { | ||
//입력 | ||
cin >> n >> m; // 사무실의 세로와 가로 입력받기 | ||
board.assign(n, vector<int>(m)); | ||
for (int i = 0; i < n; i++) { // 세로 만큼 | ||
for (int j = 0; j < m; j++) // 가로 만큼 | ||
cin >> board[i][j]; // 보드 값 입력 | ||
} | ||
|
||
//연산 | ||
backtracking(0); | ||
|
||
//출력 | ||
cout << ans; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
// | ||
// Created by user on 2021-11-16. | ||
// | ||
|
||
#include <iostream> | ||
#include <vector> | ||
|
||
using namespace std; | ||
const int INF = 1e5 * 2; //최대 n-1개의 간선을 지나게 됨 | ||
|
||
void floydWarshall(int n, vector<vector<int>> &graph, vector<vector<int>> &table) { // 간선 정보와 경로 정보 순서대로 입력받음 | ||
for (int k = 1; k <= n; k++) { // 집하장 n개 | ||
for (int i = 1; i <= n; i++) { // 집하장 n개 | ||
for (int j = 1; j <= n; j++) { // 집하장 n개 | ||
int new_dist = graph[i][k] + graph[k][j]; //중간에 k를 거쳐서 i에서 j로 감 | ||
if (new_dist < graph[i][j]) { //i->k->j가 i->j보다 빠른 경로라면 | ||
graph[i][j] = new_dist; // i->j의 간선정보 갱신 | ||
table[i][j] = table[i][k]; // i->j의 경로를 i->k의 중간 경로(table[i][k])로 갱신 | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* graph : 플로이드-워셜 결과 행렬 그래프 | ||
* table : 경로표. table[i][j] = i->j로 가기 위해 제일 먼저 거쳐야 하는 정점 | ||
* | ||
* 1. i->j의 중간 경로를 i로 초기화 | ||
* 2. i->k->j가 i->j보다 짧다면 i->j의 중간 경로를 i->k의 중간 경로(table[i][k])로 갱신 | ||
* k로 갱신하는게 아니라 table[i][k]로 갱신하는 이유는? | ||
* 만약 i->k의 경로가 i->t->k라면 최종 경로는 i->t->k->j | ||
* 바로 k로 갱신하면 t를 놓칠 수 있기 때문에 table[i][k]로 갱신 | ||
* line 16을 table[i][j] = k로 바꾸면 결과가 어떻게 되는지 확인해보세요 | ||
*/ | ||
int main() { | ||
int n, m, s, d, c; // n: 집하장 개수, m: 집하장간 경로 개수, s:start 집하장 번호, d: destination 집하장 번호, c: 오가는데 필요한 시간 | ||
|
||
//입력 | ||
cin >> n >> m; | ||
vector<vector<int>> graph(n+1, vector<int>(n+1, INF)); // 간선정보 저장 | ||
vector<vector<int>> table(n+1, vector<int>(n+1, 0)); // 경로 정보 저장 | ||
for (int i = 1; i <= n; i++) | ||
graph[i][i] = 0; // 집하장 i에서 i로 가는 데 걸리는 시간은 0 | ||
|
||
while (m--) { //무방향 그래프 | ||
cin >> s >> d >> c; // 모든 경로의 집하장과 소요시간 입력 | ||
//간선 정보 | ||
graph[s][d] = graph[d][s] = c; // 집하장 s와 d를 오가는데 필요한 시간은 c | ||
|
||
//경로 정보 (1. table[i][j]에서 i->j의 중간 경로를 i로 초기화 => 제일 먼저 거쳐야 하는 정점은 j) | ||
table[s][d] = d; // s -> d로 가기 위해 제일 먼저 거쳐야 하는 정점: d | ||
table[d][s] = s; // d -> s로 가기 위해 제일 먼저 거쳐야 하는 정점: s | ||
} | ||
|
||
//연산 | ||
floydWarshall(n, graph, table); | ||
|
||
//출력 | ||
for (int i = 1; i <= n; i++) { | ||
for (int j = 1; j <= n; j++) { // 집하장 n개 -> table 크기: n X n | ||
if (i == j) // table[i][i]에는 | ||
cout << "- "; // '-' 출력 | ||
else // 그 외의 경우에는 | ||
cout << table[i][j] << ' '; // 경로 정보 출력 | ||
} | ||
cout << '\n'; // 구분선 출력 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
// | ||
// Created by user on 2021-11-16. | ||
// | ||
|
||
#include <iostream> | ||
#include <vector> | ||
|
||
using namespace std; | ||
const int SIZE = 19; | ||
|
||
//범위와 돌의 종류가 유효한지 확인 | ||
bool promising(int r, int c, int stone, vector<vector<int>> &board) { | ||
return r >= 0 && r < SIZE && c >= 0 && c < SIZE && board[r][c] == stone; | ||
// r과 c가 모두 오목판의 범위 내에 있고, 보드 값이 stone 값과 같을 때만 true 반환 | ||
} | ||
// 육목 아니고, 확실히 이겼는지 확인하는 함수 | ||
bool validDir(int r, int c, int d, int stone, vector<vector<int>> &board) { | ||
// 구해야 하는 돌은 가장 왼쪽 => 가로, 세로, 우하향 대각선, 우상향 대각선 | ||
int dr[4] = {0, 1, 1, -1}; // row 값 | ||
int dc[4] = {1, 0, 1, 1}; // col 값 | ||
|
||
//(r, c) 이전에 위치한 이어지는 돌이 있나? => (r, c) 왼쪽 방향 | ||
bool is_six = promising(r - dr[d], c - dc[d], stone, board); | ||
|
||
int cnt = 0; // 돌이 연속하는 횟수 | ||
while (cnt < 6 && promising(r, c, stone, board)) { //(r, c)를 가장 왼쪽으로 하는 이어지는 바둑알의 개수 | ||
cnt++; // 연속 횟수 추가 | ||
r += dr[d]; // r값 갱신 | ||
c += dc[d]; // c값 갱신 | ||
} | ||
return cnt == 5 && !is_six; // 오목이고, 육목이 아닐 경우 true 반환 | ||
} | ||
// 이겼는지 판단하는 함수 | ||
bool isEnd(int r, int c, vector<vector<int>> &board) { | ||
for (int i = 0; i < 4; i++) { //가로, 세로, 우하향 대각선, 우상향 대각선 | ||
if (validDir(r, c, i, board[r][c], board)) // true반환하면 이긴거임 | ||
return true; // 이겼으니 true | ||
} | ||
return false; // 이기지 않음 | ||
} | ||
|
||
/** | ||
* 1. 특정 좌표(r, c)를 가장 왼쪽으로 하는 가능한 모든 오목 배치에 대해 오목 여부 확인 | ||
* 가능한 모든 배치 : 오른쪽, 아래, 우하향, 우상향 | ||
* 2. 육목이상이 되는 경우 : (r, c) 왼쪽에 같은 돌이 있는 경우 | ||
*/ | ||
int main() { | ||
// 보드판 생성 | ||
vector<vector<int>> board(SIZE, vector<int>(SIZE, 0)); | ||
|
||
//입력 | ||
for (int i = 0; i < SIZE; i++) { | ||
for (int j = 0; j < SIZE; j++) | ||
// 오목판 입력. 검: 1, 흰: 2, 빈: 0 | ||
cin >> board[i][j]; | ||
} | ||
|
||
//연산 & 출력 | ||
for (int i = 0; i < SIZE; i++) { // 오목판크기만큼 돌면서 | ||
for (int j = 0; j < SIZE; j++) { | ||
if (!board[i][j]) //돌이 없음 (board값 == 0일때) | ||
continue; | ||
if (isEnd(i, j, board)) { //누군가 이겼나? | ||
cout << board[i][j] << '\n' << i + 1 << ' ' << j + 1; | ||
// 이겼다면 돌의 색 출력하고 i와 j가 0부터 시작하니까 둘에다가 1을 더한 값을 출력한다. | ||
return 0; | ||
} | ||
} | ||
} | ||
cout << 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
// | ||
// Created by user on 2021-11-16. | ||
// | ||
|
||
#include <iostream> | ||
#include <vector> | ||
#include <algorithm> | ||
|
||
using namespace std; | ||
|
||
int n, m; | ||
//상, 좌, 좌상향 | ||
int dr[3] = {-1, 0, -1}; | ||
int dc[3] = {0, -1, -1}; | ||
|
||
//역추적 | ||
string back(string str1, vector<vector<int>> &path) { | ||
string result = ""; // 결과 저장할 변수 | ||
int r = n, c = m; // | ||
while (path[r][c] != -1) { | ||
int d = path[r][c]; | ||
if (d == 2) //좌상향에서 가져온 경우 -> str1[r - 1] == str2[c - 1] | ||
result += str1[r - 1]; // | ||
r += dr[d]; //역추적 | ||
c += dc[d]; //역추적 | ||
} | ||
reverse(result.begin(), result.end()); // 공통 문자열 다시 순서대로 정렬 | ||
return result; // 공통 문자열 리턴 | ||
} | ||
|
||
//LCS 길이 구하는 함수 | ||
int lcs(string str1, string str2, vector<vector<int>> &path) { | ||
vector<vector<int>> dp(n + 1, vector<int>(m + 1, 0)); // 두 문자열의 길이만큼의 2차원 벡터 생성 | ||
for (int i = 1; i <= n; i++) { // 첫번째 문자열 길이만큼 반복 | ||
for (int j = 1; j <= m; j++) { // 두번째 문자열 길이만큼 반복 | ||
if (str1[i - 1] != str2[j - 1]) { //두 문자가 서로 다르면 | ||
dp[i][j] = dp[i - 1][j]; //우선 위쪽 값 가져온 것으로 저장 | ||
path[i][j] = 0; //경로(방향) 저장 | ||
if (dp[i][j] < dp[i][j - 1]) { //왼쪽이 더 크다면 | ||
dp[i][j] = dp[i][j - 1]; // 왼쪽 값으로 저장 | ||
path[i][j] = 1; //경로(방향) 저장 | ||
} | ||
} else if (str1[i - 1] == str2[j - 1]) { //두 문자가 서로 같다면 | ||
dp[i][j] = dp[i - 1][j - 1] + 1; // 좌측 상향 대각선보다 1 큰 값 저장(해당 문자들이 포함되기 전에 길이 + 1) | ||
path[i][j] = 2; //경로(방향) 저장 | ||
} | ||
} | ||
} | ||
return dp[n][m]; //LCS 길이 리턴 | ||
} | ||
|
||
/** | ||
* 기본 문제: LCS (해당 풀이는 "08. 동적계획법.pdf" 참고) | ||
* | ||
* [역추적] | ||
* - 위쪽, 왼쪽, 좌상향 중 어느 방향에서 왔는지 경로를 저장한 후, 역추적하는 문제 | ||
* - 경로 저장은 dp배열이 갱신될 때 함 | ||
* | ||
* 해당 풀이는 인덱스를 편하게 관리하기 위해 dp와 path 배열을 (1, 1)부터 시작 | ||
*/ | ||
|
||
int main() { | ||
string str1, str2; | ||
|
||
//입력 | ||
cin >> str1 >> str2; // 두 문자열 입력 | ||
n = str1.length(); // 첫번째 문자열의 길이 | ||
m = str2.length(); // 두번째 문자열의 길이 | ||
vector<vector<int>> path(n + 1, vector<int>(m + 1, -1)); //그 전 방향 저장 | ||
|
||
//연산 | ||
int ans = lcs(str1, str2, path); //lcs | ||
string result = back(str1, path); //역추적 | ||
|
||
//출력 | ||
cout << ans << '\n' << result << '\n'; | ||
return 0; | ||
} |