728x90

한윤정이 이탈리아에 가서 아이스크림을 사먹는데 

1 초 128 MB 11014 4434 3345 40.306%

문제

한윤정과 친구들은 이탈리아로 방학 여행을 갔다. 이탈리아는 덥다. 윤정이와 친구들은 아이스크림을 사먹기로 했다. 아이스크림 가게에는 N종류의 아이스크림이 있다. 모든 아이스크림은 1부터 N까지 번호가 매겨져있다. 어떤 종류의 아이스크림을 함께먹으면, 맛이 아주 형편없어진다. 따라서 윤정이는 이러한 경우를 피하면서 아이스크림을 3가지 선택하려고 한다. 이때, 선택하는 방법이 몇 가지인지 구하려고 한다.

입력

첫째 줄에 정수 N과 M이 주어진다. N은 아이스크림 종류의 수이고, M은 섞어먹으면 안 되는 조합의 개수이다. 아래 M개의 줄에는 섞어먹으면 안 되는 조합의 번호가 주어진다. 같은 조합은 두 번 이상 나오지 않는다. (1 ≤ N ≤ 200, 0 ≤ M ≤ 10,000)

출력

첫째 줄에, 가능한 방법이 총 몇 개 있는지 출력한다.

예제 입력 1

5 3
1 2
3 4
1 3

예제 출력 1

3

힌트

5개의 아이스크림과 3가지 섞어먹으면 안 되는 조합이 있다. 1번은 2번 3번과 섞으면 안되고, 3번은 4번과 섞으면 안 된다. 따라서 (1 4 5), (2 3 5), (2 4 5)와 같이 3가지 방법이 있다.

 

 

재귀를 사용한 백트래킹 기법으로 해결이 가능한 문제다.

먼저 입력에 맞춰서 섞으면 안되는 아이스크림을 bool타입 2차원 배열의 형태로 저장한다.

예를들어 1번과 3번 아이스크림을 섞어 먹을 수 없다면, cantMix[1][3] = true;, cantMix[3][1] = true; 와 같이 2차원배열에 저장해두고 1번 아이스크림부터 N번 아이스크림까지 반복문을 돌면서 고를 수 있는 아이스크림 3개를 골라 경우의 수를 카운팅하면 된다.

 

아이스크림을 고를 수 있는지 없는지 확인하기 위해 현재까지 고른 아이스크림을 저장하기 위한 배열을 별도로 만들고, 배열에 포함된 아이스크림들(현재까지 선택한 아이스크림들)과 고르고 싶은 아이스크림을 섞을 수 있는지 비교한 후 저장한다. 이 과정을 끝까지 반복하면 선택가능한 모든 경우의 수를 계산할 수 있다.

 

위 아이디어를 코드로 옮기면 아래와 같다.

 

#include <bits/stdc++.h>
#define endl '\n'
using namespace std;
int n,m,result = 0;
bool cantMix[201][201]; // 수의 범위가 작으므로 맵보다 배열이 유리
vector<int> selected;

/**
 * 같이 먹으면 안되는 아이스크림 조합 생성
 * 앞에서부터 아이스크림을 하나 고르고, 그 아이스크림과 함께 골라도 되는 경우만 세어서 3개 고를때까지 반복 => 숫자 세기
*/
// 몇개 골랐는지, 어떤 인덱스까지 탐색했는지(다음 인덱스부터 탐색)
void selectIce(int start){
    if(selected.size()==3){ // 3개를 골랐다면
        result++;
        return;
    }
    for(int i=start+1;i<n+1;i++){ // 1번부터 n번까지의 아이스크림에 대해
        // i번째 아이스크림을 현재 집합에 넣을 수 있는지 확인
        bool able = true; // 넣을 수 있음
        // 현재 집합에 포함된 수들과 합쳐서 넣어도 되는지 확인
        for(int j=0;j<selected.size();j++){
            if(cantMix[i][selected[j]]){ // i와 selected의 j번째 인덱스의 인자를 섞을 수 없다면
                able = false;
                break;
            }
        }
        if(able){ // 넣을수 있다면
            selected.push_back(i);
            selectIce(i);
            selected.pop_back();
        } 
    }
}


int main(){
    cin.tie(NULL) -> ios::sync_with_stdio(false);
    cin>>n>>m;
    int a,b;
    for(int i=0;i<m;i++){
        cin>>a>>b;
        // 섞어먹으면 안되는 조합들
        cantMix[a][b] = true;
        cantMix[b][a] = true;
    }
    selectIce(0); // 아이스크림 고르기
    cout<<result<<endl;
}

 

https://www.acmicpc.net/problem/2422

 

2422번: 한윤정이 이탈리아에 가서 아이스크림을 사먹는데

첫째 줄에 정수 N과 M이 주어진다. N은 아이스크림 종류의 수이고, M은 섞어먹으면 안 되는 조합의 개수이다. 아래 M개의 줄에는 섞어먹으면 안 되는 조합의 번호가 주어진다. 같은 조합은 두 번

www.acmicpc.net

728x90

도영이가 만든 맛있는 음식 

1 초 128 MB 15352 8516 6219 54.022%

문제

도영이는 짜파구리 요리사로 명성을 날렸었다. 이번에는 이전에 없었던 새로운 요리에 도전을 해보려고 한다.

지금 도영이의 앞에는 재료가 N개 있다. 도영이는 각 재료의 신맛 S와 쓴맛 B를 알고 있다. 여러 재료를 이용해서 요리할 때, 그 음식의 신맛은 사용한 재료의 신맛의 곱이고, 쓴맛은 합이다.

시거나 쓴 음식을 좋아하는 사람은 많지 않다. 도영이는 재료를 적절히 섞어서 요리의 신맛과 쓴맛의 차이를 작게 만들려고 한다. 또, 물을 요리라고 할 수는 없기 때문에, 재료는 적어도 하나 사용해야 한다.

재료의 신맛과 쓴맛이 주어졌을 때, 신맛과 쓴맛의 차이가 가장 작은 요리를 만드는 프로그램을 작성하시오.

입력

첫째 줄에 재료의 개수 N(1 ≤ N ≤ 10)이 주어진다. 다음 N개 줄에는 그 재료의 신맛과 쓴맛이 공백으로 구분되어 주어진다. 모든 재료를 사용해서 요리를 만들었을 때, 그 요리의 신맛과 쓴맛은 모두 1,000,000,000보다 작은 양의 정수이다.

출력

첫째 줄에 신맛과 쓴맛의 차이가 가장 작은 요리의 차이를 출력한다. 

예제 입력 1

1
3 10

 

예제 출력 1

7

예제 입력 2

2
3 8
5 8

예제 출력 2

1

예제 입력 3

4
1 7
2 6
3 8
4 9

예제 출력 3

1

2, 3, 4번 재료를 사용한다면, 요리의 신맛은 2×3×4=24, 쓴맛은 6+8+9=23이 된다. 차이는 1이다.

 

 

전형적인 브루트포스 문제이다. 재료를 1개부터 n개 선택하는 모든 경우의 수에 대하여, 파이썬 라이브러리 itertools의 combination을 이용하여

신맛과 쓴맛을 계산한후, 두 값의 차이를 기존의 최소값과 비교하는 과정을 반복해주면 된다.

 

예를 들어 주어진 재료(n)가 3개있다고 가정한다면, 재료를 1개부터 3개까지 고를 수 있을것이고 1개를 고를때 나올 수 있는 조합과, 2개를 고를때 나올 수 있는 조합, 3개를 고를 때 나올 수 있는 조합에서 신맛은 서로 곱하고, 쓴맛은 서로 합하여 최종 신맛과 쓴맛을 구한후에 두 값의 차를 절대값으로 하여 기존의 최소값과 비교하는 과정을 반복하는것이다.

 

위 아이디어를 코드로 옮기면 다음과 같다.

import sys
from itertools import combinations
n = int(input())
# 재료 정보 (신맛, 쓴맛) 입력받기
food = [list(map(int, input().split())) for _ in range(n)]

result = sys.maxsize  # 신맛과 쓴맛의 최소값

# 재료를 1개부터 n개 선택하는 모든 경우의 수
for i in range(1, n+1):
    # 재료를 i개 선택하는 모든 경우 => selected_food
    for selected_food in combinations(food, i):
        # 신맛, 쓴맛 초기화
        sour, written = 1, 0
        # 선택된 재료들을 대상으로 신맛 쓴맛 계산
        for s, b in selected_food:
            sour *= s  # 신맛은 재료들의 곱
            written += b  # 쓴맛은 재료들의 합
        # 최종적으로 쓴맛과 신맛의 차가 가장 적게 되어야함
        # 기존 최소값과 비교하여 작은값 업데이트
        result = min(abs(sour-written), result)

# 모든 경우의 수를 돌려봤을 때
# 쓴맛과 신맛의 최소값 출력
print(result)
 

 

728x90

비밀번호 

1 초 64 MB 2081 786 605 43.525%

문제

웅찬이는 근성이 대단한 도둑이다. 그래서 금고를 털 때, 모든 조합을 눌러본다. 예를 들어 비밀번호가 3글자 라는 사실을 알 때, 000, 001, 002, 003, … 998, 999의 모든 조합을 눌러본다. 그러나 웅찬이는 선견지명이 있어서 비밀번호에 어떤 숫자가 들어가는지 일부 알 수 있다. 예를 들어 3글자 비밀번호에 0이 들어감을 안다면 999 와 같이 0이 들어가지 않는 수는 가능성이 없다. 그러나 000, 012, 030과 같은 수는 가능하다. 비밀번호의 길이와 선견지명으로 알게된 비밀번호의 일부 숫자가 주어질 때, 모든 가능한 비밀번호의 개수를 출력하는 프로그램을 작성하시오.

입력

첫줄에 비밀번호의 길이 n (1 ≤ n ≤ 7), 선견지명으로 알게된 비밀번호에 들어가는 수 m(0 ≤ m ≤ n) 이 주어지고, 둘째 줄에 m개의 서로 다른 숫자(0~9)가 주어진다. m이 0인 경우 둘째 줄은 주어지지 않는다.

출력

가능한 모든 비밀번호의 개수를 출력한다.

예제 입력 1 복사

2 1
7

예제 출력 1 복사

19

예제 입력 2 복사

2 2
3 4

예제 출력 2 복사

2

힌트

첫 번째 예제의 경우 가능한 비밀번호의 조합은 07, 17, 27, 37, 47, 57, 67, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 87, 97이다. 두 번째 예제의 경우 가능한 비밀번호의 조합은 34, 43이다.

 

처음 문제를 접근했던 방식은 '선견지명'으로 알고 있는 숫자들을 미리 배치한 뒤, 남은 숫자들을 배치하는 것이었다.

하지만 위 방법으로 접근하게되면 알고리즘이 지나치게 복잡하게 된다는 단점이 있었고 생각을 단순화하여 수를 배치한 뒤 '선견지명'으로 알고있는 숫자들을 포함하는지 확인하는 방식으로 방향을 틀었다.

 

숫자들을 중복으로 포함할수 있기 때문에, '중복 순열'을 사용해야 하며 파이썬은 itertools의 product를 사용하여 이를 쉽게 구현 할 수 있다.

 

위 아이디어를 적용한 코드는 아래와 같다.

# https://www.acmicpc.net/problem/13908
from itertools import product  # 중복 순열
'''
중복 순열을 이용하여 만들 수 있는 모든 숫자를 만들어보고
만들어진 숫자가 '이미 알고 있는 수'를 포함한다면 result +=1
'''
result = 0
numbers = [i for i in range(10)]
n, m = map(int, input().split())
if m == 0:
    known = []
else:
    known = list(map(int, input().split()))  # 이미 알고 있는 수

for number_array in product(numbers, repeat=n):  # n개의 수 뽑기
    # 만들어진 수가 known을 모두 포함하는지 확인
    error = False
    for number in known:
        # 뽑은 숫자 배열에 known의 모든 수가 포함되어 있는지 확인
        if number not in number_array:  # 포함되어 있지 않다면
            error = True
            break
    if not error:  # 문제없이 known의 모든 수를 포함하였다면
        result += 1


print(result)

 

위 문제에서 주의해야 할 점은 m이 0으로 주어지는 경우, 알고있는 수에 대한 입력이 없다는 점이다. 

조건식을 통해 m이 0인 경우 빈 배열로 처리해야 하며, 수를 만든 이후, 수를 하나씩 비교하면서 '이미 알고있는 수'의 숫자가 모두 만들어진 수에 포함되는지 boolean 변수를 통해 확인하고 문제없다면 result를 +1 한다.

 

https://www.acmicpc.net/problem/13908

 

13908번: 비밀번호

첫 번째 예제의 경우 가능한 비밀번호의 조합은 07, 17, 27, 37, 47, 57, 67, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 87, 97이다. 두 번째 예제의 경우 가능한 비밀번호의 조합은 34, 43이다.

www.acmicpc.net

 

+ Recent posts