레이블이 Dijkstra 알고리즘인 게시물을 표시합니다. 모든 게시물 표시
레이블이 Dijkstra 알고리즘인 게시물을 표시합니다. 모든 게시물 표시

2018년 9월 2일 일요일

SW Expert Academy - 1차 문제

1858 모범 택시 드라이버 [D6]
3421 수제 버거 장인 [D5]
3503 초보자를 위한 점프대 배치하기 [D5]
4747 사막에서 만난 지니 [D6]
3082 화면 보호기 [D7] https://www.acmicpc.net/problem/3990

1. 1858 모범 택시 드라이버 
N개의 도시와 M개의 도로가 있다. 
각 도로는 일방통행이며 제한속도 V와 도로의 길이 L이 주어진다.
V가 0인경우에는 이전속도를 유지하며 달린다.
0번도시에서 70의 속도로 출발할 때 D까지 도달하는 최단 시간 경로를 구하는 문제이다.
============================================================================

도로를 이동할 때는 제한속도 V만큼의 속도로 가는것이 가장 최단시간에 갈 수 있는것을 알 수 있다.
각 도시를 도달할때의 속도가 다르고 시간또한 다르기 때문에 정점을 N*V개 놓고 다익스트라를 
돌리면 된다.
#pragma GCC optimize ("O3")
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 155;
const int MAXM = 505;
#define INF (987654321)
int T, n, m, d;
vector<pair<intpair<intint>>> adj[MAXN];        //e, v, l
double dist[MAXN][MAXM];
pair<int,int> par[MAXN][MAXM];
void dijkstra() {
    for (int i = 0; i < n; i++for (int j = 0; j < MAXM; j++) dist[i][j] = INF;
    priority_queue<pair<doublepair<intint>>> pq;
    
    dist[0][70= 0;
    pq.push({ 0,{700} });
    while (!pq.empty()) {
        double t = -pq.top().first;
        int v = pq.top().second.first;
        int here = pq.top().second.second;
        pq.pop();
        if (here == d) return;
        for (pair<intpair<intint>> x : adj[here]) {
            int nxt = x.first;
            int limit = x.second.first;
            int len = x.second.second;
            if (limit == 0) limit = v;
            double cost = len / (double)limit;
            if (dist[nxt][limit] > t + cost) {
                dist[nxt][limit] = t + cost;
                pq.push({ -dist[nxt][limit], {limit, nxt} });
                par[nxt][limit] = { here, v };
            }
        }
    }
}
int main() {
    scanf("%d"&T);
    for (int t = 1; t <= T; t++) {
        scanf("%d%d%d"&n, &m, &d);
        for (int i = 0; i < MAXN; i++) adj[i].clear();
        for (int i = 0; i < m; i++) {
            int s, e, v, l;
            scanf("%d%d%d%d"&s, &e, &v, &l);
            adj[s].push_back({ e,{v,l} });
        }
        dijkstra();
        double v = INF;
        int st = -1;
        for (int i = 1; i < MAXM; i++) {
            if (dist[d][i] < v) {
                v = dist[d][i];
                st = i;
            }
        }
        stack<int> stk;
        for (pair<intint> x = { d, st };; x = par[x.first][x.second]) {
            stk.push(x.first);
            if (x.first == 0 && x.second == 70break;
        }
        printf("#%d ", t);
        while (!stk.empty()) printf("%d ", stk.top()), stk.pop();
        puts("");
    }
    return 0;
}
cs

2. 3421 수제 버거 장인
N개의 재료가 있고 M개의 궁합이 맞지않는 쌍이 주어진다.
N개의 재료들로 만들 수 있는 조합의 개수를 구하는 문제이다.
============================================================================

처음에는 $O(2^NM)$의 복잡도로 제출했는데 통과가 되긴하였다. 
사실 $O(2^N)$풀이가 존재한다.

각각의 재료마다 궁합이 맞지않는 다른 재료들을 bit mask형태로 저장해 놓는다.
$2^N$의 경우를 보며 현재 재료를 넣어도 되는 경우에만 넣으면 된다.
#pragma GCC optimize ("O3"
#include <bits/stdc++.h>
using namespace std;
int T, n, m, cnt;
int no[20];
void solve(int idx, int info) {
    if (idx == n) {
        cnt++;
        return;
    }
    if (!(no[idx] & info)) solve(idx + 1, info | (1 << idx));
    solve(idx + 1, info);
}
int main() {
    scanf("%d"&T);
    for (int t = 1; t <= T; t++) {
        cnt = 0;
        scanf("%d%d"&n, &m);
        for (int i = 0; i < n; i++) no[i] = 0;
        for (int i = 0; i < m; i++) {
            int u, v;
            scanf("%d%d"&u, &v);
            u--; v--;
            no[u] |= (1 << v);
            no[v] |= (1 << u);
        }
        solve(00); 
        printf("#%d %d\n", t, cnt);
    }
    return 0;
}
cs

3. 3503 초보자를 위한 점프대 배치하기
N개의 막대를 재배치하여 두 막대의 최대 높이차가 최소화하는 문제이다.
============================================================================


직관적으로 정렬을 시키고 가우시안 분포처럼 막대를 배치하는것이 최적일거 같은 느낌이든다.
실제로도 답이고 정확한 증명은 좀더 고민을 해봐야 겠다.

doju님의 증명 힌트 : 
그 직관적으로 나오는 답에서 간격 상한을 더 줄였을 때 사이클이 생길 수 없음을
증명하는 방향으로 시도해 보세요.

#pragma GCC optimize ("O3"
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 5;
int T, n, arr[MAXN];
int tmp[MAXN];
int main() {
    scanf("%d"&T);
    for (int t = 1; t <= T; t++) {
        scanf("%d"&n);
        for (int i = 0; i < n; i++scanf("%d"&arr[i]);
        sort(arr, arr + n);
        int l = 0, r = n - 1;
        for (int i = 0; i < n; i++) {
            if (~i & 1) tmp[l++= arr[i];
            else tmp[r--= arr[i];
        }
        int ans = abs(tmp[0- tmp[n - 1]);
        for (int i = 0; i < n - 1; i++) ans = max(ans, abs(tmp[i] - tmp[i + 1]));
        printf("#%d %d\n", t, ans);
    }
    return 0;
}
cs

4. 4747 사막에서 만난 지니
N개의 수열이 주어졌을 때 각각의 합이 같도록 3가지로 분류하는 방법을 구하는 문제이다.
즉, [1, 4, 2, 3, 2]가 주어진다면 [1, 3], [2, 2], [4]로 분류할 수 있다.
또한 수열의 값들은 균등하게 분포되있다.
============================================================================

N이 사실상 2,000이여서 마땅한 방법을 생각해내기 어렵다.
푸는 방법도 다양한 방법이 존재할것 같은 문제다.

우선 각각의 분류에는 (수열의 합 / 3)만큼이 할당되어야 할 것이다.
나는 각각의 분류를 동시에 채우는 것이 아니라 하나의 분류를 채운 후에 
나머지 분류들을 채우는 방식으로 구현하였다.
채울 때에는 큰 수부터 작은수를 넣고 채우는것이 불가능하면 back-tracking하여 
다시 탐색을 진행한다.
시간 복잡도는 $O(3^N)$이다. 수열의 값이 균등하여 최악의 시간내에는 답이 나온다.
#pragma GCC optimize ("O3"
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 2005;
int T, n;
int arr[MAXN];
bool visit[MAXN];
int s;
vector<int> ans[3];
bool f;
void solve(int xdi, int idx, int sum) {
    if (xdi == 2) {
        f = true;
        return;
    }
    if (sum == s) {
        solve(xdi + 1, n - 10);
        return;
    }
    if (idx < 0return;
    if (visit[idx]) {
        solve(xdi, idx - 1, sum);
        return;
    }
    if (f) return;
    if (sum + arr[idx] <= s) {
        visit[idx] = true;
        ans[xdi].push_back(arr[idx]);
        solve(xdi, idx - 1, sum + arr[idx]);
        if (f) return;
        ans[xdi].pop_back();
        visit[idx] = false;
    }
    if (f) return;
    solve(xdi, idx - 1, sum);
}
int main() {
    scanf("%d"&T);
    for (int t = 1; t <= T; t++) {
        memset(visit, 0sizeof visit);
        f = false;
        scanf("%d"&n);
        s = 0;
        for (int i = 0; i < 3; i++) ans[i].clear();
        for (int i = 0; i < n; i++scanf("%d"&arr[i]), s += arr[i];
        s /= 3;
        solve(0, n - 10);
        printf("#%d\n", t);
        for (int i = 0; i < 2; i++) {
            for (int j = 0; j < ans[i].size(); j++printf("%d ", ans[i][j]);
            puts("");
        }
        for (int i = 0; i < n; i++if (!visit[i]) printf("%d ", arr[i]);
        puts("");
    }
    return 0;
}
cs

5. 3082 화면 보호기 

풀지 못했다. 좀더 고민해봐야겠다 ㅠㅠ

2017년 8월 22일 화요일

1486 등산

1486 등산 https://www.acmicpc.net/problem/1486

2차원 지도가 주어진다.
세준이는 [0,0]에서 시작하여 인접한 좌표중 차이가 T이하인 곳으로 이동할 수 있다.
높은곳으로 이동하는 경우는 차이의 제곱만큼, 아닌 경우는 1만큼 시간이 소모된다.
D초 이하의 시간동안 다시 돌아올 수 있는 위치중 최고 높은 위치를 구하는 문제이다.

1. 다익스트라 풀이
[0,0]에서 갈 수 있는 최단경로들을 구하고 반대로 [0,0]으로 올 수 있는 최단 경로들을 구한다.
돌아오는 경우도 [0,0]에서 돌리되 주어진 정보와 반대로 cost를 주어야 한다.
#include <cstdio>
#include <cmath>
#include <cstring>
#include <vector>
#include <queue>
#include <algorithm>
#include <functional>
using namespace std;
inline int ipow(int x) { return x*x; }
int N, M, T, D;
char map[25][25];
int imap[25][25];
int dy[] = { -1,0,0,1 };
int dx[] = { 0,1,-1,0 };
int d[626], rd[626];
vector<pair<intint>> adj[626], rev_adj[626];
void solve(vector<pair<intint>> vect[626], int dist[626]) {
    priority_queue<pair<intint>> pq;
    pq.push({ 0,0 });
    dist[0= 0;
    while (!pq.empty()) {
        int here = pq.top().second;
        int time = -pq.top().first;
        pq.pop();
        if (dist[here] < time) continue;
        for (auto n : vect[here]) {
            int next = n.second;
            if (dist[next] > time + n.first) {
                dist[next] = time + n.first;
                pq.push({ -dist[next], next });
            }
        }
    }
}
int main() {
    memset(d, 0x3fsizeof d);
    memset(rd, 0x3fsizeof rd);
    scanf("%d%d%d%d"&N, &M, &T, &D);
    for (int n = 0;n < N;n++scanf("%s"&map[n]);
    for (int n = 0;n < N;n++) {
        for (int m = 0;m < M;m++) {
            if (map[n][m] >= 'A' && map[n][m] <= 'Z')
                imap[n][m] = map[n][m] - 'A';
            else
                imap[n][m] = map[n][m] - 'a' + 26;
        }
    }
    for (int n = 0;n < N;n++) {
        for (int m = 0;m < M;m++) {
            for (int i = 0;i < 4;i++) {
                int y = n + dy[i], x = m + dx[i];
                if (y < 0 || y >= N || x < 0 || x >= M) continue;
                int start = imap[n][m], end = imap[y][x];
 
                if (abs(start - end> T) continue;
                if (start >= end) adj[n*+ m].emplace_back(make_pair(1, y*+ x));
                else adj[n*+ m].emplace_back(make_pair(ipow(start - end), y*+ x));
                if(start > end) rev_adj[n*+ m].emplace_back(make_pair(ipow(start - end), y*+ x));
                else rev_adj[n*+ m].emplace_back(make_pair(1, y*+ x));
 
            }
        }
    }
    solve(adj, d);
    solve(rev_adj, rd);
    int ans = imap[0][0];
 
    for (int n = 0;n < N;n++) {
        for (int m = 0;m < M;m++) {
            if (d[n*+ m] + rd[n*+ m] <= D) {
                ans = max(ans, imap[n][m]);
            }
        }
    }
    printf("%d\n", ans);
    return 0;
}
cs
2. 플로이드 와샬 풀이
최대로 정점이 들어온다면 $O(N^{3}M^{3})$의 복잡도를 가지지만 시간안에 통과된다.
방법은 위의 방법과 동일하다.
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
#define INF 987654321
int N, M, T, D;
char map[26][26];
int adj[666][666];
int dy[] = { -1,0,0,1 }, dx[] = { 0,1,-1,0 };
int getAlpha(int y, int x) {
    if (map[y][x] >= 'a' && map[y][x] <= 'z') {
        return map[y][x] - 'a' + 26;
    }
    else{
        return map[y][x] - 'A';
    }
}
int main() {
    scanf("%d%d%d%d"&N, &M, &T, &D);
    for (int n = 0;n < N;n++)
        scanf("%s", map[n]);
    for (int n = 0;n < N*M;n++)
        for (int m = 0;m < N*M;m++)
            adj[n][m] = n == m ? 0 : INF;
    for (int n = 0;n < N;n++) {
        for (int m = 0;m < M;m++) {
            int here = n*+ m;
            for (int i = 0;i < 4;i++) {
                int y = n + dy[i], x = m + dx[i];
                if (y < 0 || y >= N || x < 0 || x >= M) continue;
                int next = y*+ x;
                int diff = getAlpha(n, m) - getAlpha(y, x);
 
                if (abs(diff) > T) continue;
                if (diff > 0) {        //내리막
                    adj[here][next] = 1;
                }
                else if (diff < 0) {        //오르막
                    adj[here][next] = diff*diff;
                }
                else {
                    adj[here][next] = 1;
                }
            }
        }
    }
    int MAX = N*M;
    for (int k = 0;k < MAX;k++) {
        for (int n = 0;n < MAX;n++) {
            for (int m = 0;m < MAX;m++) {
                if (adj[n][m] > adj[n][k] + adj[k][m]) {
                    adj[n][m] = adj[n][k] + adj[k][m];
                }
            }
        }
    }
 
    int ans = 0;
    for (int n = 0;n < MAX;n++) {
        if (adj[0][n] + adj[n][0<= D) {
            ans = max(ans, getAlpha(n / M, n%M));
        }
    }
    printf("%d\n", ans);
    return 0;
}
cs