2017년 10월 25일 수요일

2017 삼성 S/W 역량 테스트 기출문제

14888 연산자 끼워넣기 https://www.acmicpc.net/problem/14888
14889 스타트와 링크 https://www.acmicpc.net/problem/14889
14890 경사로 https://www.acmicpc.net/problem/14890
14891 톱니바퀴 https://www.acmicpc.net/problem/14891

삼성 코딩 테스트 대부분은 bfsdfs, 구현, 완전탐색 정도의 문제들이 많이 출제된다.
이 포스트에서는 17년 삼성 DS, CE/IM 기출문제를 다뤄보겠다.

1. 연산자 끼워넣기 
N개의 수가 주어지고 N-1개의 연산자가 주어진다.
수 사이에 연산자를 끼워놓을 때 연산자 우선순위없이 결과값의 최댓값, 최솟값을 구하는 문제다.

N이 작으므로 완전탐색으로 답을 구할 수 있다.
#include <cstdio>
#include <algorithm>
using namespace std;
int N;
int arr[11];
int Plus, Minus, Mul, Mod;
int Min = 1000000001, Max = -1000000001;
void solve(int idx, int sum, int plus, int minus, int mul, int mod) {
    if (idx == N) {
        Min = min(Min, sum);
        Max = max(Max, sum);
        return;
    }
    if (plus) solve(idx + 1, sum + arr[idx], plus - 1, minus, mul, mod);
    if (minus) solve(idx + 1, sum - arr[idx], plus, minus - 1, mul, mod);
    if (mul) solve(idx + 1, sum*arr[idx], plus, minus, mul - 1, mod);
    if (mod) solve(idx + 1, sum / arr[idx], plus, minus, mul, mod - 1);
}
int main() {
    scanf("%d"&N);
    for (int n = 0;n < N;n++scanf("%d"&arr[n]);
    scanf("%d%d%d%d"&Plus, &Minus, &Mul, &Mod);
    solve(1, arr[0], Plus, Minus, Mul, Mod);
    printf("%d\n%d\n", Max, Min);
    return 0;
}
cs

2. 스타트와 링크
N명의 사람들을 N/2로 이루어진 두 팀으로 나눌 때 팀의 능력치의 차이 최소값을 구하는 문제다.
팀의 능력치는 팀에 속한 사람들 쌍의 능력치의 합이다.

dp, 완전탐색으로 풀 수 있다.
dp로 풀었을 때는 BOJ기준으로 시간이 1초 넘게 소모된다.
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
#define INF 987654321
int N;
int dp[1 << 20];
int map[21][21];
bool count(int info) {
    int cnt = 0;
    for (int n = 0;n < N;n++) {
        if (info & (1 << n)) cnt++;
    }
    return cnt == N / 2;
}
int getScore(int info) {
    int start = 0, link = 0;
    for (int n = 0;n < N;n++) {
        if (info & (1 << n)) {
            for (int m = n + 1;m < N;m++) {
                if (info & (1 << m)) {
                    start += (map[n][m] + map[m][n]);
                }
            }
        }
        else {
            for (int m = n + 1;m < N;m++) {
                if (!(info & (1 << m))) {
                    link += (map[n][m] + map[m][n]);
                }
            }
        }
    }
    return abs(start - link);
}
int solve(int info) {
    if (count(info)) return dp[info] = getScore(info);
    int &ret = dp[info];
    if (ret != -1return ret;
    ret = INF;
    for (int n = 0;n < N;n++) {
        if (info & (1 << n)) continue;
        ret = min(ret, solve(info | (1 << n)));
    }
    return ret;
}
int main() {
    memset(dp, -1sizeof dp);
    scanf("%d"&N);
    for (int n = 0;n < N;n++)for (int m = 0;m < N;m++scanf("%d"&map[n][m]);
    printf("%d\n", solve(0));
    return 0;
}
cs

완전탐색으로 하는게 더 빠르다.
#include <cstdio>
inline int min(int a, int b) { return a < b ? a : b; }
inline int abs(int x) { return x < 0 ? -x : x; }
int N;
int map[20][20];
int ans = 0x3f3f3f3f;
void solve(int a, int b, int st1, int st2, int info1, int info2, int pivot) {
    if (a + b == N) {
        ans = min(ans, abs(st1 - st2));
        return;
    }
    int plus = 0;
    if (a < N / 2) {
        for (int m = 0;m < N;m++) {
            if (info1 & (1 << m)) plus += (map[pivot][m] + map[m][pivot]);
        }
        solve(a + 1, b, st1 + plus, st2, (info1 | (1 << pivot)), info2, pivot + 1);
    }
    if (b < N / 2) {
        plus = 0;
        for (int m = 0;m < N;m++) {
            if (info2 & (1 << m)) plus += (map[pivot][m] + map[m][pivot]);
        }
        solve(a, b + 1, st1, st2 + plus, info1, (info2 | (1 << pivot)), pivot + 1);
    }
}
int main() {
    scanf("%d"&N);
    for (int n = 0;n < N;n++)for (int m = 0;m < N;m++scanf("%d"&map[n][m]);
    solve(0000000);
    printf("%d\n", ans);
    return 0;
}
cs

3. 경사로
문제 요약 생략

시작점에서 경사로만큼 내려가거나 올라갈 수 있는경우, 높이가 같은경우로 이동시키듯 구현했다.
나머지 자잘한 예외처리도 해줘야한다.
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int N, L;
int map[101][101];
bool v[101];
int ans = 0;
void solve() {
    for (int n = 0;n < N;n++) {
        memset(v, falsesizeof v);
        int prev = map[n][0];
        int s = 1;
        while (s) {
            if (s == N) { ans++break; }
            if (map[n][s] == prev) s++;
            else {
                if (map[n][s] == prev - 1) {        //감소
                    if (s + L - 1 >= N) break;
                    bool go = true;
                    for (int m = s;m <= s + L - 1;m++) {
                        if (map[n][m] != map[n][s] || v[m]) { go = falsebreak; }
                    }
                    if (go) {
                        for (int m = s;m <= s + L - 1;m++) v[m] = true;
                        s = s + L, prev--;
                    }
                    else break;
                }
                else if (map[n][s] == prev + 1) {        //증가
                    if (s - L < 0break;
                    bool go = true;
                    for (int m = s - L;m < s;m++) {
                        if (map[n][m] != map[n][s] - 1 || v[m]) { go = falsebreak; }
                    }
                    if (go) {
                        for (int m = s - L;m < s;m++) v[m] = true;
                        s = s + 1, prev++;
                    }
                    else break;
                }
                else break;
            }
        }
    }
}
int main() {
    scanf("%d%d"&N, &L);
    for (int n = 0;n < N;n++for (int m = 0;m < N;m++scanf("%d"&map[n][m]);
    solve();
    for (int n = 0;n < N;n++for (int m = n + 1;m < N;m++) swap(map[n][m], map[m][n]);
    solve();
    printf("%d\n", ans);
    return 0;
}
cs

4. 톱니바퀴
문제 요약 생략

젤 재밌었던 문제다.
bitmask에 저장해서 회전하는것을 shift연산으로 해줬다.
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 4, M = 8;
int Q;
int s[4= { 0 };
void change(int num, int dir) {
    if (dir == 1) {
        int save = (s[num] & (1 << 7)) > 0 ? 1 : 0;
        s[num] <<= 1;
        if (save && !(s[num] & (1 << 0))) s[num] |= (1 << 0);
    }
    else {
        int save = (s[num] & (1 << 0)) > 0 ? 1 : 0;
        s[num] >>= 1;
        if (save && !(s[num] & (1 << 7))) s[num] |= (1 << 7);
    }
}
//dir : 시계 or 반시계 or 정지
//왼쪽
void solve1(int num, int dir) {
    if (num == -1return;
    int prev = (s[num + 1& (1 << 6)) > 0 ? 1 : 0;
    int here = (s[num] & (1 << 2)) > 0 ? 1 : 0;
    if (prev == here) return;
    else {
        solve1(num - 1-dir);
        change(num, -dir);
    }
}
//오른쪽
void solve2(int num, int dir) {
    if (num == N) return;
    int prev = (s[num - 1& (1 << 2)) > 0 ? 1 : 0;
    int here = (s[num] & (1 << 6)) > 0 ? 1 : 0;
    if (prev == here) return;
    else {
        solve2(num + 1-dir);
        change(num, -dir);
    }
}
int main() {
    for (int n = 0;n < N;n++) {
        int in;
        for (int m = 0;m < M;m++) {
            scanf("%1d"&in);
            if (in) s[n] |= (1 << m);
        }
    }
    scanf("%d"&Q);
    while (Q--) {
        int num, dir;
        scanf("%d%d"&num, &dir);
        num--;
        solve1(num - 1, dir);
        solve2(num + 1, dir);
        change(num, dir);
    }
    int ans = 0;
    for (int n = 0, score = 1;n < N;n++, score *= 2) {
        if (s[n] & (1 << 0)) ans += score;
    }
    printf("%d\n", ans);
    return 0;
}
cs

댓글 6개:

  1. 어쩐지.. 많이 푸셨다 싶었는데..
    삼성 기출이였네요..

    답글삭제
    답글
    1. 넹ㅋㅋ 갓백준님이 엄청 빨리 올려주셨더라구요

      삭제
    2. 경사로가 살짝 까다롭긴 한데요..
      좌측에서 우측으로 가면서 증가하는 block에 대해서 체크해 주고
      우측에서 좌측으로 가면서 증가하는 block에 대해서 체크해 주면서 풀면 상당히 쉽게 풀리더라고요.
      아마 차이를 최소로라는 문제 풀이가 그런 식일 겁니다.

      연산자 끼워넣기는 php로 풀려다가 TLE가 계속 나서 포기하고
      그냥 c++로 풀었네요..

      삭제
    3. 경사로가 제일 까다로왔던 문제인거같네요

      삭제
    4. 행렬 dp도 연습해야 하는데..
      (tree dp도 못하면서 무슨 matrix dp.. ㅠㅠ)

      그래도 tree dp 중에서 SNS 사회망 서비스를 푸니 행복하더군요..

      삭제
    5. 행렬 곱셈 최소화랑 파일 합치기문제였나?
      이게 행렬dp인가 보네영
      요즘 tree dp문제 찾아 풀려하는데 12995번 한번 풀어보세여
      좋은 문제인거같은데 아직 안풀었네요 ㅋㅋ..

      삭제