diff --git "a/11\354\233\224 9\354\235\274 \354\266\224\352\260\200\354\240\234\354\266\234/12852.cpp" "b/11\354\233\224 9\354\235\274 \354\266\224\352\260\200\354\240\234\354\266\234/12852.cpp" new file mode 100644 index 0000000..6807835 --- /dev/null +++ "b/11\354\233\224 9\354\235\274 \354\266\224\352\260\200\354\240\234\354\266\234/12852.cpp" @@ -0,0 +1,66 @@ +/* + * 1로 만들기 2 : https://www.acmicpc.net/problem/12852 + */ + +#include +#include + +using namespace std; + +//역추적 +vector back(int x, vector &path) { //x = n부터 역추적 시작 + vector result(0); // 역추적한 루트 저장 + while (x != 0) { //맨 처음 시작 경로가 나올 때 까지 + result.push_back(x); // 역추적한 값 넣고 + x = path[x]; //x는 다음 패스값으로 갱신 + } + return result; //역추적 루트 리턴 +} + +//1로 만드는 최소 연산 횟수 리턴 +int makeOne(int n, vector &path) { + vector dp(n + 1, 0); //dp용 배열 dp[i] = (i에 가능한 연산을 적용한 수 중 최소 연산 횟수) + 1 + for (int i = 2; i <= n; i++) { //dp 1~n까지!! 1부터 시작해서 n까지 간다. + int min_value = dp[i - 1]; //우선 i - 1 연산 적용한 것으로 최소 연산 횟수 저장 + path[i] = i - 1; //i-1 연산 적용한 것으로 생각하고 바로 1 작은 수 + if (!(i % 3) && min_value > dp[i / 3]) { //3으로 나눌 수 있고, 현재 가정해 놓은 최소 연산 회수보다 더 적게 가능하면 + min_value = dp[i / 3]; //min_value 갱신 + path[i] = i / 3; //path 갱신 + } + if (!(i % 2) && min_value > dp[i / 2]) { //2 나눌 수 있고, 현재 가정해 놓은 최소 연산 회수보다 더 적게 가능하면 + min_value = dp[i / 2]; //min_value 갱신 + path[i] = i / 2;//path 갱신 + } + dp[i] = min_value + 1; // min_value에서 연산 하나 적용해서 바로 온 거니깐. min_value+1 + } + return dp[n]; //n에 가능한 연산을 적용한 수 중 최소 연산 횟수 +} + +/** + * 기본 문제: 1로 만들기 + * + * [점화식] + * dp[i] = (i에 가능한 연산을 적용한 수 중 최소 연산 횟수) + 1 + * dp[i] = min(dp[i / 3], dp[i / 2], dp[i - 1]) + 1 + * + * [역추적] + * path: 인덱스가 정수를 나타냄, 해당 수에서 연산을 적용한 다음 수를 저장 + * n부터 역추적 시작 + */ + +int main() { + int n; + //입력 + cin >> n; + vector path(n + 1, 0); //경로 저장 + + //연산 + int ans = makeOne(n, path); //n을 1로 만드는 최소 연산 횟수 //path에 연산 적용한 수 계속 쌓으면서 + vector result = back(n, path); // path를 n부터 역추적해서 벡터에 담기. + + //출력 + cout << ans << '\n'; //최소 연산 횟수 + for (int i = 0; i < result.size(); i++) //역추적한 경로 + cout << result[i] << ' '; + return 0; +} \ No newline at end of file diff --git "a/11\354\233\224 9\354\235\274 \354\266\224\352\260\200\354\240\234\354\266\234/1719.cpp" "b/11\354\233\224 9\354\235\274 \354\266\224\352\260\200\354\240\234\354\266\234/1719.cpp" new file mode 100644 index 0000000..3f94ec3 --- /dev/null +++ "b/11\354\233\224 9\354\235\274 \354\266\224\352\260\200\354\240\234\354\266\234/1719.cpp" @@ -0,0 +1,68 @@ +/* + * 택배 : https://www.acmicpc.net/problem/1719 + */ +#include +#include + +using namespace std; +const int INF = 1e5 * 2; //최대 n-1개의 간선을 지나게 됨 + +//플로이드-워셜 +void floydWarshall(int n, vector> &graph, vector> &table) { + for (int k = 1; k <= n; k++) { //k : 거치는 노드 + for (int i = 1; i <= n; i++) { //2차원 배열 모두 살피면서 (각 노드에서 각 노드로) + for (int j = 1; j <= n; j++) { + 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로 가기 위해 제일 먼저 거쳐야 하는 정점!! + } + } + } + } +} + +/** + * 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; + //입력 + cin >> n >> m; //집하장의 개수(노드), 집하장간 경로 개수(엣지) + vector> graph(n + 1, vector(n + 1, INF)); //플로이드-워셜 결과 행렬 그래프 + vector> table(n + 1, vector(n + 1, 0)); //경로표. table[i][j] = i->j로 가기 위해 제일 먼저 거쳐야 하는 정점 + for (int i = 1; i <= n; i++) + graph[i][i] = 0; //자기자신 + + while (m--) { //무방향 그래프 + cin >> s >> d >> c; + //간선 정보 + graph[s][d] = graph[d][s] = c; // s->d(d->s)로 가는데 가중치 c + + //경로 정보 + table[s][d] = d; //s->d로 가기 위해 제일 먼저 거쳐야 할 정점 + table[d][s] = s; //d->s로 가기 위해 제일 먼저 거쳐야 할 정점 + } + + //연산 + floydWarshall(n, graph, table); //플로이드-워샬 계산, table에 경로 저장 + + //출력 + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= n; j++) { + if (i == j) //자기 자신 + cout << "- "; + else //경로 출력 + cout << table[i][j] << ' '; + } + cout << '\n'; + } +} \ No newline at end of file diff --git "a/11\354\233\224 9\354\235\274 \354\266\224\352\260\200\354\240\234\354\266\234/9019.cpp" "b/11\354\233\224 9\354\235\274 \354\266\224\352\260\200\354\240\234\354\266\234/9019.cpp" new file mode 100644 index 0000000..9a28c76 --- /dev/null +++ "b/11\354\233\224 9\354\235\274 \354\266\224\352\260\200\354\240\234\354\266\234/9019.cpp" @@ -0,0 +1,81 @@ +/* + * DSLR : https://www.acmicpc.net/problem/9019 + */ + +#include +#include +#include +#include + +using namespace std; +typedef pair ci; // {숫자, 명령어} +const int SIZE = 10000; //A와 B는 모두 10,000 미만 + +//역추적 +string back(int x, vector &path) { //x = B부터 역추적 시작 + string ans = ""; + while (path[x].first != -1) { //맨 처음 넣어둔 시작 경로가 될 때까지 + ans += path[x].second; // 명령어 역추적해서 앞에서부터 쌓음 + x = path[x].first; //숫자 갱신 + } + reverse(ans.begin(), ans.end()); // 명령어 역추적해서 앞에서부터 쌓았으므로 리버스 해줌 + return ans; // 역추적한 명렁어 리턴 +} + +//bfs +void bfs(int a, int b, vector &path) { + vector visited(SIZE, false); //이미 썼던 숫자인지. + queue q; //큐에 노드(숫자값) 저장 + q.push(a); //시작 노드 초기화 + visited[a] = true; //시작 노드(숫자값) 초기화 + while (!q.empty()) { //bfs 탐색 진행 + int cur = q.front(); //현재 수 + q.pop(); // pop 안 하면 무한루프! + + if (cur == b) //B를 만들었다면 즉시 탐색 종료 + break; + + //차례대로 D,S,L,R 명령어를 실행했을 때 path (first : 경로(숫자), second : 명령어) + vector child = {{cur * 2 % SIZE, 'D'}, //각 명렁어의 수행 결과를 사칙연산으로 경로 나타낼 수 있음 + {(cur - 1 + SIZE) % SIZE, 'S'}, + {(cur * 10 % SIZE) + (cur / 1000), 'L'}, + {(cur % 10 * 1000) + (cur / 10), 'R'}}; + + //각 명령어를 모두 본다. + for (int i = 0; i < 4; i++) { + int next = child[i].first; //다음 숫자. + if (!visited[next]) { //방문한 적 없으면 + q.push(next); // 큐에 추가해주고 + visited[next] = true; //방문 체크 + path[next] = ci(cur, child[i].second); //자식 노드에 부모 노드(경로)와 명령어 저장 + } + } + } +} + +/** + * path: 경로와 수행한 명령어(D, S, L, R)를 저장 + * + * 각 명령어의 수행 결과를 사칙연산으로 나타낼 수 있음 (cur: 현재 수, SIZE = 10,000) + * D: cur * 2 % SIZE + * S: (cur - 1 + SIZE) % SIZE (cur이 0일 경우를 처리하기 위해) + * L: (cur * 10 % SIZE) + (cur / 1000) + * R: (cur % 10 * 1000) + (cur / 10) + * + * 따라서 위의 연산을 적용한 값들을 자식노드로 하여 bfs 탐색 진행 + * 앞서 구한 path값을 따라 B인덱스부터 역추적 시작 + */ + +int main() { + int t, a, b; + + //입력 & 연산 & 출력 + cin >> t; //테스트케이스 + while (t--) { + cin >> a >> b; + vector path(SIZE, {-1, ' '}); //first: 경로, second: 명령어 + bfs(a, b, path); //a -> b 경로 bfs 실행 + cout << back(b, path) << '\n'; // 최단 경로 역추적! + } + return 0; +} \ No newline at end of file