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 화면 보호기 

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

댓글 없음:

댓글 쓰기