728x90

주사위 굴리기
www.acmicpc.net/problem/14499

 

14499번: 주사위 굴리기

첫째 줄에 지도의 세로 크기 N, 가로 크기 M (1 ≤ N, M ≤ 20), 주사위를 놓은 곳의 좌표 x y(0 ≤ x ≤ N-1, 0 ≤ y ≤ M-1), 그리고 명령의 개수 K (1 ≤ K ≤ 1,000)가 주어진다. 둘째 줄부터 N개의 줄에 지도

www.acmicpc.net

문제

크기가 N×M인 지도가 존재한다. 지도의 오른쪽은 동쪽, 위쪽은 북쪽이다. 이 지도의 위에 주사위가 하나 놓여져 있으며, 주사위의 전개도는 아래와 같다. 지도의 좌표는 (r, c)로 나타내며, r는 북쪽으로부터 떨어진 칸의 개수, c는 서쪽으로부터 떨어진 칸의 개수이다. 

2 4 1 3 5 6

주사위는 지도 위에 윗 면이 1이고, 동쪽을 바라보는 방향이 3인 상태로 놓여져 있으며, 놓여져 있는 곳의 좌표는 (x, y) 이다. 가장 처음에 주사위에는 모든 면에 0이 적혀져 있다.

지도의 각 칸에는 정수가 하나씩 쓰여져 있다. 주사위를 굴렸을 때, 이동한 칸에 쓰여 있는 수가 0이면, 주사위의 바닥면에 쓰여 있는 수가 칸에 복사된다. 0이 아닌 경우에는 칸에 쓰여 있는 수가 주사위의 바닥면으로 복사되며, 칸에 쓰여 있는 수는 0이 된다.

주사위를 놓은 곳의 좌표와 이동시키는 명령이 주어졌을 때, 주사위가 이동했을 때 마다 상단에 쓰여 있는 값을 구하는 프로그램을 작성하시오.

주사위는 지도의 바깥으로 이동시킬 수 없다. 만약 바깥으로 이동시키려고 하는 경우에는 해당 명령을 무시해야 하며, 출력도 하면 안 된다.

입력

첫째 줄에 지도의 세로 크기 N, 가로 크기 M (1 ≤ N, M ≤ 20), 주사위를 놓은 곳의 좌표 x y(0 ≤ x ≤ N-1, 0 ≤ y ≤ M-1), 그리고 명령의 개수 K (1 ≤ K ≤ 1,000)가 주어진다.

둘째 줄부터 N개의 줄에 지도에 쓰여 있는 수가 북쪽부터 남쪽으로, 각 줄은 서쪽부터 동쪽 순서대로 주어진다. 주사위를 놓은 칸에 쓰여 있는 수는 항상 0이다. 지도의 각 칸에 쓰여 있는 수는 10을 넘지 않는 자연수 또는 0이다.

마지막 줄에는 이동하는 명령이 순서대로 주어진다. 동쪽은 1, 서쪽은 2, 북쪽은 3, 남쪽은 4로 주어진다.

출력

이동할 때마다 주사위의 윗 면에 쓰여 있는 수를 출력한다. 만약 바깥으로 이동시키려고 하는 경우에는 해당 명령을 무시해야 하며, 출력도 하면 안 된다.


삼성SW역량테스트 기출문제 4번째 문제

주사위를 굴려서 주사위 값, 지도의 값을 갱신하는 문제

 

우선 주사위를 굴려서 나올 수 있는 경우의수를 보면

주사위는 6면으로 이루어져 있으므로

위의 방향 순서대로 아래와 같이 배열을 채웠을때

int dice[6] = {1, 2, 3, 4, 5, 6};

 

case 1:동쪽으로 굴리기

    dice = { dice[4], dice[2], dice[1], dice[6], dice[5], dice[3] };

case 2:서쪽으로 굴리기

    dice = { dice[3], dice[2], dice[6], dice[1], dice[5], dice[4] };

case 3:북쪽으로 굴리기

    dice = { dice[5], dice[1], dice[3], dice[4], dice[6], dice[2] };

case 4:남쪽으로 굴리기

    dice = { dice[2], dice[6], dice[3], dice[4], dice[1], dice[5] };

 

다음과 같이 변한다고 볼 수있다.

 

머리 좋은사람은 바로바로 계산이 되나??? 나는 직접 그림으로 그려서 경우의 수를 구했다.

아 그리고 이분의 코드를 따라해 보았습니다.

donggoolosori.github.io/2020/11/09/boj-14499/

 

[백준] 14499번 주사위 굴리기 - C++ - DGOS | 동꿀오소리

문제 14499번 주사위 굴리기 풀이 우선 주사위 각면의 숫자를 저장하는 크기 7의 dice 벡터를 만든다. 인덱스 번호를 맞추기 위해 크기를 7로 할당했다. 1 vector dice(7); // index 1 윗면, 2 북쪽면, 3

donggoolosori.github.io


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include <iostream>
#include <vector>
using namespace std;
int map[21][21];
int n, m, y, x, k;
int ans;
//index  1(동쪽) 2(서쪽) 3(북쪽) 4(남쪽)
//문제에 나와있는 순서대로
int dx[] = { 01-100 };
int dy[] = { 000-11 };
 
//움직임을 담는 배열
int moves[1001];
 
//위, 북, 동, 서, 남, 아래
vector<int> dice(7);
void move(int dir)
{
    //좌표움직임 설정한 순서대로
    switch (dir)
    {
        //동
    case 1:
        dice = { 0, dice[4], dice[2], dice[1], dice[6], dice[5], dice[3] };
        break;
        //서
    case 2:
        dice = { 0, dice[3], dice[2], dice[6], dice[1], dice[5], dice[4] };
        break;
        //북
    case 3:
        dice = { 0, dice[5], dice[1], dice[3], dice[4], dice[6], dice[2] };
        break;
        //남
    case 4:
        dice = { 0, dice[2], dice[6], dice[3], dice[4], dice[1], dice[5] };
        break;
    }
}
void solve()
{
    for (int i = 0; i < k; i++)
    {
        int ny = y + dy[moves[i]];
        int nx = x + dx[moves[i]];
        //범위를 벗어나는지 확인
        if (ny >= 0 && nx >= 0 && ny < n && nx < m)
        {
            //현재좌표 업데이트
            y = ny; x = nx;
            //움직인다.
            move(moves[i]);
            //주사위 윗면 출력
            cout << dice[1<< '\n';
            //지도가 0이면 지도에 주사위 아래 숫자 복사
            if (!map[y][x])
                map[y][x] = dice[6];
            //지도가 0이 아니면 주사위에 지도 숫자 복사, 지도 숫자-> 0
            else
            {
                dice[6= map[y][x];
                map[y][x] = 0;
            }
        }
    }
}
int main()
{
    cin >> n >> m >> y >> x >> k;
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < m; j++)
        {
            cin >> map[i][j];
        }
    }
    for (int i = 0; i < k; i++)
        cin >> moves[i];
 
    solve();
    return 0;
}
cs

 

LIST
728x90

최소 스패닝 트리

문제

그래프가 주어졌을 때, 그 그래프의 최소 스패닝 트리를 구하는 프로그램을 작성하시오.

최소 스패닝 트리는, 주어진 그래프의 모든 정점들을 연결하는 부분 그래프 중에서 그 가중치의 합이 최소인 트리를 말한다.

입력

첫째 줄에 정점의 개수 V(1 ≤ V ≤ 10,000)와 간선의 개수 E(1 ≤ E ≤ 100,000)가 주어진다. 다음 E개의 줄에는 각 간선에 대한 정보를 나타내는 세 정수 A, B, C가 주어진다. 이는 A번 정점과 B번 정점이 가중치 C인 간선으로 연결되어 있다는 의미이다. C는 음수일 수도 있으며, 절댓값이 1,000,000을 넘지 않는다.

그래프의 정점은 1번부터 V번까지 번호가 매겨져 있고, 임의의 두 정점 사이에 경로가 있다. 최소 스패닝 트리의 가중치가 -2,147,483,648보다 크거나 같고, 2,147,483,647보다 작거나 같은 데이터만 입력으로 주어진다.

출력

첫째 줄에 최소 스패닝 트리의 가중치를 출력한다.

 


 

개념 : 방향없는 그래프에서 모든 노드를 포함하면서, 순환되는 경로를 제거한 현태를 스패닝 트리(Spanning Tree) 라고 한다. 이 스패일 트리에서 가중치의 합을 최소로 만드는 스패닝 트리를 최소 스패닝트리(Minimal Spanning Tree)라고 한다. 최소 스패닝 트리를 만들기 위한 방법으로는 크루스칼 알고리즘을 이용한다!

 

크루스칼 알고리즘에 대해서는 한빛미디어 - 이것이코딩테스트다 with python 을 참고하시길 바랍니다.

www.youtube.com/watch?v=Gj7s-Nrt1xE&list=PLVsNizTWUw7H9_of5YCB0FmsSc-K44y81&index=35&ab_channel=%ED%95%9C%EB%B9%9B%EB%AF%B8%EB%94%94%EC%96%B4

unionParent, findParent는 여러 알고리즘을 풀면서 종종 이용해 먹을만한 개념이라고 생각한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int n, m;
 
int parent[10001];
int ans;
struct node {
    int val, x, y;
    node(int val, int x, int y)
    {
        this->val = val;
        this->= x;
        this->= y;
    }
};
vector<node> v;
int findParent(int x)
{
    if (x == parent[x]) return x;
    else return parent[x] = findParent(parent[x]);
}
 
bool sameParent(int x, int y)
{
    x = findParent(x);
    y = findParent(y);
    if (x == y)return true;
    else return false;
}
 
void unionParent(int x, int y)
{
    x = findParent(x);
    y = findParent(y);
    if (x > y) parent[x] = y;
    else parent[y] = x;
}
bool compare(node& a, node& b)
{
    return a.val < b.val;
}
int main()
    cin >> n >> m;
    for (int i = 0; i < m; i++)
    {
        int a, b, c;
        cin >> a >> b >> c;
        v.push_back(node( c,a,b ));
    } 
    sort(v.begin(), v.end(), compare);
    for (int i = 0; i < n; i++)
        parent[i] = i;
 
    for (int i = 0; i < v.size(); i++)
    {
        if (!sameParent(v[i].x, v[i].y))
        {
            unionParent(v[i].x, v[i].y);
            ans += v[i].val;
        }
    }
    cout << ans;
    
    return 0;
}
cs
LIST
728x90

최단경로 

 

1 초 256 MB 87849 23730 11414 23.534%

문제

방향그래프가 주어지면 주어진 시작점에서 다른 모든 정점으로의 최단 경로를 구하는 프로그램을 작성하시오. 단, 모든 간선의 가중치는 10 이하의 자연수이다.

입력

첫째 줄에 정점의 개수 V와 간선의 개수 E가 주어진다. (1≤V≤20,000, 1≤E≤300,000) 모든 정점에는 1부터 V까지 번호가 매겨져 있다고 가정한다. 둘째 줄에는 시작 정점의 번호 K(1≤K≤V)가 주어진다. 셋째 줄부터 E개의 줄에 걸쳐 각 간선을 나타내는 세 개의 정수 (u, v, w)가 순서대로 주어진다. 이는 u에서 v로 가는 가중치 w인 간선이 존재한다는 뜻이다. u와 v는 서로 다르며 w는 10 이하의 자연수이다. 서로 다른 두 정점 사이에 여러 개의 간선이 존재할 수도 있음에 유의한다.

출력

첫째 줄부터 V개의 줄에 걸쳐, i번째 줄에 i번 정점으로의 최단 경로의 경로값을 출력한다. 시작점 자신은 0으로 출력하고, 경로가 존재하지 않는 경우에는 INF를 출력하면 된다.


이 문제는 dijkstra의 대표적인 문제로 start 노드에서 모든 노드로 가는 최소 비용(최단 거리)를 구하는 알고리즘 입니다.

여기에 우선 순위 큐에 추가, 삭제 하는 시간은 O(logE) 시간 이므로 총 시간 복잡도는 O(ElogE) 입니다.

이때 |E|  <= |V|2 이므로,O(Elog|V|)라고볼수도있습니다.

 

주의 사항으로는 priority_queue<pair<int,int>> pq; 로 선언하여 pq.push({a,b}); 라고 넣을때 a 부분의 가증치를 두어서

가중치가 낮은 것부터 탐색할 수 있도록 해야 합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <iostream>
#include <vector>
#include <queue>
#define INF 1e9
using namespace std;
 
int v, e, start;
int d[20001];
vector< pair<int,int>> ve[20001];
void dijkstra(int start)
{
    priority_queue<pair<intint>> pq;
    pq.push({ 0, start });
    d[start] = 0;
    while (!pq.empty())
    {
        int dist = -pq.top().first;
        int now = pq.top().second;
        pq.pop();
        if (d[now] < dist)
            continue;
        for (int i = 0; i < ve[now].size(); i++)
        {
            int cost = dist + ve[now][i].first;
            if (d[ve[now][i].second] > cost)
            {
                d[ve[now][i].second] = cost;
                pq.push({-cost, ve[now][i].second });
            }
        }
    }
    return;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
 
    cin >> v >> e >> start;
    for (int i = 0; i < e; i++)
    {
        int a, b, c;
        cin >> a >> b >> c;
        ve[a].push_back({ c,b });
    }
    fill(d, d + 20001, INF);
    dijkstra(start);
    for (int i = 1; i <= v; i++)
    {
        if (d[i] == INF)
            cout << "INF" << '\n';
        else
            cout << d[i] << '\n';
    }
    return 0;
}
cs
LIST
728x90

연구소

문제

인체에 치명적인 바이러스를 연구하던 연구소에서 바이러스가 유출되었다. 다행히 바이러스는 아직 퍼지지 않았고, 바이러스의 확산을 막기 위해서 연구소에 벽을 세우려고 한다.

연구소는 크기가 N×M인 직사각형으로 나타낼 수 있으며, 직사각형은 1×1 크기의 정사각형으로 나누어져 있다. 연구소는 빈 칸, 벽으로 이루어져 있으며, 벽은 칸 하나를 가득 차지한다. 

일부 칸은 바이러스가 존재하며, 이 바이러스는 상하좌우로 인접한 빈 칸으로 모두 퍼져나갈 수 있다. 새로 세울 수 있는 벽의 개수는 3개이며, 꼭 3개를 세워야 한다.

예를 들어, 아래와 같이 연구소가 생긴 경우를 살펴보자.

2 0 0 0 1 1 0 0 0 1 0 1 2 0 0 1 1 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 1 0 1 0 0 0 0 0 0 1 0 0 0 0 0

이때, 0은 빈 칸, 1은 벽, 2는 바이러스가 있는 곳이다. 아무런 벽을 세우지 않는다면, 바이러스는 모든 빈 칸으로 퍼져나갈 수 있다.

2행 1열, 1행 2열, 4행 6열에 벽을 세운다면 지도의 모양은 아래와 같아지게 된다.

2 1 0 0 1 1 0 1 0 1 0 1 2 0 0 1 1 0 1 0 0 0 1 0 0 0 1 0 0 0 0 0 0 1 1 0 1 0 0 0 0 0 0 1 0 0 0 0 0

바이러스가 퍼진 뒤의 모습은 아래와 같아진다.

2 1 0 0 1 1 2 1 0 1 0 1 2 2 0 1 1 0 1 2 2 0 1 0 0 0 1 2 0 0 0 0 0 1 1 0 1 0 0 0 0 0 0 1 0 0 0 0 0

벽을 3개 세운 뒤, 바이러스가 퍼질 수 없는 곳을 안전 영역이라고 한다. 위의 지도에서 안전 영역의 크기는 27이다.

연구소의 지도가 주어졌을 때 얻을 수 있는 안전 영역 크기의 최댓값을 구하는 프로그램을 작성하시오.

입력

첫째 줄에 지도의 세로 크기 N과 가로 크기 M이 주어진다. (3 ≤ N, M ≤ 8)

둘째 줄부터 N개의 줄에 지도의 모양이 주어진다. 0은 빈 칸, 1은 벽, 2는 바이러스가 있는 위치이다. 2의 개수는 2보다 크거나 같고, 10보다 작거나 같은 자연수이다.

빈 칸의 개수는 3개 이상이다.

출력

첫째 줄에 얻을 수 있는 안전 영역의 최대 크기를 출력한다.


이번 문제는 acmicpc 의 그래프 이론문제 중에서 연구소를 풀어보겠습니다.

 

n  x  m 의 배열에 0은 빈칸 1은 벽 2는 바이러스 입니다.

벽이 가로막혀 있지 않은한 바이러스가 계속 퍼져 나가는데 3개의 벽을 임의로 설치해서 바이러스가 최소로 퍼져나갈 수 있도록 많드는 문제입니다.

 

1. 3개의 벽을 임의로 구성하는데는 DFS를 이용하였고, 

2. 3개의 벽을 설치하였을때 바이러스가 퍼지도록 BFS 를 이용해 보았습니다.


 

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
//0 : 빈칸, 1: 벽, 2:바이러스
int n, m, ans;
int dx[] = { 00-11 };
int dy[] = { 1-100 };
int a[8][8];
int b[8][8];
queue<pair<intint>> virus_q;
//바이러스 퍼뜨리기
int virus()
{
    while (!virus_q.empty())
    {
        int y = virus_q.front().first;
        int x = virus_q.front().second;
        virus_q.pop();
        for (int i = 0; i < 4; i++)
        {
            int nx = x + dx[i]; int ny = y + dy[i];
            if (nx >= 0 && ny >= 0 && nx < m && ny < n)
            {
                if (b[ny][nx] == 0)
                {
                    virus_q.push({ ny,nx });
                    b[ny][nx] = 2;
                }
            }
        }
    }
    int cnt = 0;
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < m; j++)
        {
            if (b[i][j] == 0)
                cnt++;
        }
    }
    return cnt;
}
//벽세우기
void dfs(int depth)
{
    if (depth == 3)
    {
        for (int i = 0; i < n; i++)
        {
            for (int j = 0; j < m; j++)
            {
                b[i][j] = a[i][j];
                if (b[i][j] == 2)
                    virus_q.push({ i, j });
            }
        }
        ans = max(ans, virus());
        return;
    }
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < m; j++)
        {
            if (a[i][j] == 0)
            {
                a[i][j] = 1;
                dfs(depth + 1);
                a[i][j] = 0;
            }
        }
    }
}
int main()
{
    cin >> n >> m;
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < m; j++)
        {
            cin >> a[i][j];
        }
    }
    dfs(0);
    cout << ans;
    return 0;
}
cs
LIST
728x90

부분수열의 합

문제

N개의 정수로 이루어진 수열이 있을 때, 크기가 양수인 부분수열 중에서 그 수열의 원소를 다 더한 값이 S가 되는 경우의 수를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 정수의 개수를 나타내는 N과 정수 S가 주어진다. (1 ≤ N ≤ 20, |S| ≤ 1,000,000) 둘째 줄에 N개의 정수가 빈 칸을 사이에 두고 주어진다. 주어지는 정수의 절댓값은 100,000을 넘지 않는다.

출력

첫째 줄에 합이 S가 되는 부분수열의 개수를 출력한다.


이번문제는 백트레킹! 부분수열의 합 입니다.

n개의 원소중에서 m개를 뽑았을때 합이 S 이면 결과값을 늘려나가는 방법을 이용했습니다.

여기서 m개의 범위는 1 <= m <=n 으로 하였습니다.

기본적으로 nCm (1<=m<=n) 일때 결과값이 S인 것을 찾는다고 생각하면 좋을것 같습니다.

 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int n, m;
int a[20];
vector<int> v;
int check[20];
int ans;
void dfs(int depth, int next, int k)
{
    if (depth == k)
    {
        int sum = 0;
        for (int i = 0; i < n; i++)
            sum += a[i];
        if (sum == m) ans++;
 
        return;
    }
    for (int i = next; i < n; i++)
    {
        if (!check[i])
        {
            check[i] = true;
            a[depth] = v[i];
            dfs(depth + 1, i + 1, k);
            check[i] = false;
        }
    }
}
int main()
{
    cin >> n >> m;
    for (int i = 0; i < n; i++)
    {
        int k;
        cin >> k;
        v.push_back(k);
    }
    for(int i=1;i<=n;i++)
        dfs(00, i);
    cout << ans;
    return 0;
}
cs
LIST
728x90

로또

문제

독일 로또는 {1, 2, ..., 49}에서 수 6개를 고른다.

로또 번호를 선택하는데 사용되는 가장 유명한 전략은 49가지 수 중 k(k>6)개의 수를 골라 집합 S를 만든 다음 그 수만 가지고 번호를 선택하는 것이다.

예를 들어, k=8, S={1,2,3,5,8,13,21,34}인 경우 이 집합 S에서 수를 고를 수 있는 경우의 수는 총 28가지이다. ([1,2,3,5,8,13], [1,2,3,5,8,21], [1,2,3,5,8,34], [1,2,3,5,13,21], ..., [3,5,8,13,21,34])

집합 S와 k가 주어졌을 때, 수를 고르는 모든 방법을 구하는 프로그램을 작성하시오.

입력

입력은 여러 개의 테스트 케이스로 이루어져 있다. 각 테스트 케이스는 한 줄로 이루어져 있다. 첫 번째 수는 k (6 < k < 13)이고, 다음 k개 수는 집합 S에 포함되는 수이다. S의 원소는 오름차순으로 주어진다.

입력의 마지막 줄에는 0이 하나 주어진다. 

출력

각 테스트 케이스마다 수를 고르는 모든 방법을 출력한다. 이때, 사전 순으로 출력한다.

각 테스트 케이스 사이에는 빈 줄을 하나 출력한다.



이번 문제는 조합의 응용 문제인 로또 문제를 풀어 보겠습니다.

 

이전 n과m(2)에서의 입력 조건은 N 이 주어 졌을때 1, 2, 3, .... N 이 범위라면

이번에는 n의 범위를 직접 입력해서 6개를 중복 없이 뽑는 경우의 수 입니다.

 

* 0이 입력될때까지 무한 반복

* while 실행시 사용했던 배열 초기화가 이루어져야 합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int n;
vector<int> v;
int a[49];
int check[49];
void combination(int depth, int next)
{
    if (depth == 6)
    {
        for (int i = 0; i < 6; i++)
        {
            cout << a[i] << ' ';
        }
        cout << '\n';
        return;
    }
    for (int i = next; i < n; i++)
    {
        if (!check[i])
        {
            check[i] = true;
            a[depth] = v[i];
            combination(depth + 1, i + 1);
            check[i] = false;
        }
    }
}
int main()
{
    while (true)
    {
        cin >> n;
        if (n == 0break;
        v.clear();
        fill(check, check + 490);
        fill(a, a + 490);
        for (int i = 0; i < n; i++)
        {
            int a;
            cin >> a;
            v.push_back(a);
        }
        sort(v.begin(), v.end());
        combination(00);
        cout << '\n';
    }
    return 0;
}
cs

 

LIST
728x90

연산자 끼워넣기

문제

N개의 수로 이루어진 수열 A1, A2, ..., AN이 주어진다. 또, 수와 수 사이에 끼워넣을 수 있는 N-1개의 연산자가 주어진다. 연산자는 덧셈(+), 뺄셈(-), 곱셈(×), 나눗셈(÷)으로만 이루어져 있다.

우리는 수와 수 사이에 연산자를 하나씩 넣어서, 수식을 하나 만들 수 있다. 이때, 주어진 수의 순서를 바꾸면 안 된다.

예를 들어, 6개의 수로 이루어진 수열이 1, 2, 3, 4, 5, 6이고, 주어진 연산자가 덧셈(+) 2개, 뺄셈(-) 1개, 곱셈(×) 1개, 나눗셈(÷) 1개인 경우에는 총 60가지의 식을 만들 수 있다. 예를 들어, 아래와 같은 식을 만들 수 있다.

  • 1+2+3-4×5÷6
  • 1÷2+3+4-5×6
  • 1+2÷3×4-5+6
  • 1÷2×3-4+5+6

식의 계산은 연산자 우선 순위를 무시하고 앞에서부터 진행해야 한다. 또, 나눗셈은 정수 나눗셈으로 몫만 취한다. 음수를 양수로 나눌 때는 C++14의 기준을 따른다. 즉, 양수로 바꾼 뒤 몫을 취하고, 그 몫을 음수로 바꾼 것과 같다. 이에 따라서, 위의 식 4개의 결과를 계산해보면 아래와 같다.

  • 1+2+3-4×5÷6 = 1
  • 1÷2+3+4-5×6 = 12
  • 1+2÷3×4-5+6 = 5
  • 1÷2×3-4+5+6 = 7

N개의 수와 N-1개의 연산자가 주어졌을 때, 만들 수 있는 식의 결과가 최대인 것과 최소인 것을 구하는 프로그램을 작성하시오.

입력

첫째 줄에 수의 개수 N(2 ≤ N ≤ 11)가 주어진다. 둘째 줄에는 A1, A2, ..., AN이 주어진다. (1 ≤ Ai ≤ 100) 셋째 줄에는 합이 N-1인 4개의 정수가 주어지는데, 차례대로 덧셈(+)의 개수, 뺄셈(-)의 개수, 곱셈(×)의 개수, 나눗셈(÷)의 개수이다. 

출력

첫째 줄에 만들 수 있는 식의 결과의 최댓값을, 둘째 줄에는 최솟값을 출력한다. 연산자를 어떻게 끼워넣어도 항상 -10억보다 크거나 같고, 10억보다 작거나 같은 결과가 나오는 입력만 주어진다. 또한, 앞에서부터 계산했을 때, 중간에 계산되는 식의 결과도 항상 -10억보다 크거나 같고, 10억보다 작거나 같다.

 


이번 문제는 연산자 끼워넣기 dfs, backtracking 문제입니다.

 

숫자의 개수는 N 개 연산자의 개수는 N-1개 이고 연산자의 우선순위는 맨 처음부터 입니다.

 

생각보다 간단? 하게 풀 수 있는 문제 이지만 여기서 주의깊게 확인해야 할 부분은 출력 에 나와 있는 -10억 ~ 10억 이라는 범위 입니다.

 

생각 없이 result1에 최댓값을 넣기 위해 0으로 초기화 하고 result2에는 1e9로 했다가 10분동안 에러 찾으려고 고생했네요.

 

이럴때 -10억 -1 , +10억 +1 로 초기화 할 수 있지만 c++ 에는 limits 이나 limits.h 에 INT_MAX, INT_MIN 을 이용할 수 있습니다.

 

코드는 다음과 같습니다.

 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include <iostream>
#include <vector>
#include <algorithm>
#include <limits>
using namespace std;
int n;
vector<int> v;
int _plus;
int _minus;
int _multi;
int _div;
//최댓값 저장
int result1 = INT_MIN;
//최솟값 저장
int result2 = INT_MAX;
void dfs(int depth, int ans)
{
    if (n == depth)
    {
        result1 = max(result1, ans);
        result2 = min(result2, ans);
        return;
    }
    //연산자가 남아 있으면 그 연산자에 해당하는
    //연산을 해줍니다.
    if (_plus > 0)
    {
        _plus--;
        dfs(depth + 1, ans + v[depth]);
        _plus++;
    }
    if (_minus > 0)
    {
        _minus--;
        dfs(depth + 1, ans - v[depth]);
        _minus++;
    }
    if (_multi > 0)
    {
        _multi--;
        dfs(depth + 1, ans * v[depth]);
        _multi++;
    }
    if (_div > 0)
    {
        _div--;
        dfs(depth + 1, ans / v[depth]);
        _div++;
    }
    return;
}
int main()
{
    cin >> n;
    for (int i = 0; i < n; i++)
    {
        int a;
        cin >> a;
        v.push_back(a);
    }
    cin >> _plus >> _minus >> _multi >> _div;
    
    dfs(1, v[0]);
    cout << result1 << '\n' << result2;
    return 0;
}
cs

 

LIST
728x90

N과 M (4)

문제

자연수 N과 M이 주어졌을 때, 아래 조건을 만족하는 길이가 M인 수열을 모두 구하는 프로그램을 작성하시오.

  • 1부터 N까지 자연수 중에서 M개를 고른 수열
  • 같은 수를 여러 번 골라도 된다.
  • 고른 수열은 비내림차순이어야 한다.
    • 길이가 K인 수열 A가 A1 ≤ A2 ≤ ... ≤ AK-1 ≤ AK를 만족하면, 비내림차순이라고 한다.

입력

첫째 줄에 자연수 N과 M이 주어진다. (1 ≤ M ≤ N ≤ 8)

출력

한 줄에 하나씩 문제의 조건을 만족하는 수열을 출력한다. 중복되는 수열을 여러 번 출력하면 안되며, 각 수열은 공백으로 구분해서 출력해야 한다.

수열은 사전 순으로 증가하는 순서로 출력해야 한다.


지금까지 순열, 조합, 중복 순열을 알아봤습니다.

마지막으로 중복 조합에 대해서 공부해 보겠습니다.

중복 조합에서는 next 인자의 값을 증가시키지 않고 전달하여 for문에서 다시 i=next 부터 찾도록? 구현해보았습니다.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
using namespace std;
 
int n, m, a[8], c[8];
void dfs(int depth, int next)
{
    if (depth == m)
    {
        for (int i = 0; i < m; i++)
            cout << a[i] + 1 << ' ';
        cout << '\n';
        return;
    }
    for (int i = next; i < n; i++)
    {
        a[depth] = i;
        dfs(depth + 1, i);
    }
}
int main()
{
    cin >> n >> m;
    dfs(00);
    return 0;
}
cs
LIST

+ Recent posts