문제
은진이는 발전소에서 근무한다. 은진이가 회사에서 잠깐 잘 때마다, 몇몇 발전소가 고장이난다. 게다가, 지금 은진이의 보스 형택이가 은진이의 사무실로 걸어오고 있다. 만약 은진이가 형택이가 들어오기 전까지 발전소를 고쳐놓지 못한다면, 은진이는 해고당할 것이다.
발전소를 고치는 방법은 간단하다. 고장나지 않은 발전소를 이용해서 고장난 발전소를 재시작하면 된다. 하지만, 이때 비용이 발생한다. 이 비용은 어떤 발전소에서 어떤 발전소를 재시작하느냐에 따라 다르다.
적어도 P개의 발전소가 고장나 있지 않도록, 발전소를 고치는 비용의 최솟값을 구하는 프로그램을 작성하시오.
입력
첫째 줄에 발전소의 개수 N이 주어진다. N은 16보다 작거나 같은 자연수이다. 둘째 줄부터 N개의 줄에는 발전소 i를 이용해서 발전소 j를 재시작할 때 드는 비용이 주어진다. i줄의 j번째 값이 그 값이다. 그 다음 줄에는 각 발전소가 켜져있으면 Y, 꺼져있으면 N이 순서대로 주어진다. 마지막 줄에는 P가 주어진다. 비용은 50보다 작거나 같은 음이 아닌 정수이고, P는 0보다 크거나 같고, N보다 작거나 같은 정수이다.
출력
첫째 줄에 문제의 정답을 출력한다. 불가능한 경우에는 -1을 출력한다.
예제 입력 1
3
0 10 11
10 0 12
12 13 0
YNN
3
예제 출력 1
21
🐝 나의 풀이
N=int(input()) # 발전기의 개수
cost=[list(map(int, input().split())) for _ in range(N)]
yn=input()
P=int(input())
size=2**(N-1)
dp=[[-1]*(1<<N) for _ in range(N)]
init=float('inf')
cnt=0
pNum=0
for i in range(N) : # 켜진 발전기 개수 세기
if yn[i] == 'Y' :
cnt+=1
pNum |= (1<<i) # 추가
def dfs(cnt, pNum) :
if cnt >= P :
return 0
if dp[cnt][pNum] != -1 : return dp[cnt][pNum]
dp[cnt][pNum]=init
for i in range(N) :
if pNum & (1<<i) == (1<<i) : # 포함?
for j in range(N) :
if i == j or pNum & (1<<j) == (1<<j) : continue
dp[cnt][pNum]=min(dp[cnt][pNum], dfs(cnt+1, pNum|(1<<j))+cost[i][j])
return dp[cnt][pNum]
ans=dfs(cnt, pNum)
if ans == init :
print(-1)
else :
print(ans)
비트마스크 풀이
다른 사람의 풀이를 참고했다.
실행시간이 2144ms라니 😅
# 실패
N=int(input()) # 발전소의 개수
arr=[list(map(int, input().split())) for _ in range(N)]
onoff=input() # 각 발전소가 켜져있나
P=int(input()) # 최소 P개는 고장나면 안됨
visit=[0]*N
ans=800
def dfs(y, value, depth) :
global ans
if depth == P :
ans=min(ans, value)
return
for i in range(N) :
if not visit[i] :
visit[i]=1
dfs(y, value+arr[y][i], depth+1)
visit[i]=0
for y in range(N) :
if onoff[y] == 'Y' : # 멀쩡한 발전소
visit[y]=1
dfs(y, 0, 1) # 멀쩡한 발전소 인덱스, 총 비용, 반복횟수
visit[y]=0
if ans == 800 :
print(-1) # 불가능
else :
print(ans)
제출하니 시간초과 ㅠㅠ
멀쩡한 발전소로 P개를 채울때까지 재귀호출한다.
return 후 visit[i]=0을 해야 다른 경우를 또 구할 수 있다.
🎀 다른 사람 풀이
// https://loosie.tistory.com/230
import java.io.*;
import java.util.*;
public class Main{
static int[][] dp;
static int[][] cost;
static int n;
static int p;
static int init=1_023_525_232;
public static void main(String[] args) throws Exception {
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
n=Integer.parseInt(br.readLine());
cost=new int[n][n];
dp=new int[n+1][1<<16];
for(int i=0; i<n; i++){
StringTokenizer st=new StringTokenizer(br.readLine());
for(int j=0; j<n; j++){
cost[i][j]=Integer.parseInt(st.nextToken());
}
}
for(int i=0; i<n+1; i++){
Arrays.fill(dp[i], -1);
}
String[] status=br.readLine().split("");
p=Integer.parseInt(br.readLine());
int pos=0;
int cnt=0;
for(int i=0; i<status.length; i++){
if(status[i].equals("Y")){
pos=pos|(1<<i);
cnt++;
}
}
int res=dfs(cnt, pos);
System.out.println(res == init ? -1 : res);
}
static int dfs(int cnt, int pNum){ // 현재 켜진 발전기 수, 현재 켜진 발전기 번호
if(cnt >= p) return 0;
if(dp[cnt][pNum] != -1) return dp[cnt][pNum];
dp[cnt][pNum]=init;
for(int i=0; i<n; i++){
// pNum 발전소가 켜져있을 때
if((pNum & (1<<i)) == (1<<i)){
for(int j=0; j<n; j++){
// 같은 번호의 발전소거나 j도 켜진 발전소면 continue
if((i==j) || (pNum&(1<<j)) == (1<<j)) continue;
dp[cnt][pNum]=Math.min(dp[cnt][pNum], dfs(cnt+1, pNum|(1<<j))+cost[i][j]);
}
}
}
return dp[cnt][pNum];
}
}
비트마스크 풀이
Arrays.fill로 편하게 2차원 배열의 행값을 -1로 초기화한다.
문제 출처 👉 백준
'coding test' 카테고리의 다른 글
[파이썬] 2098. 외판원순회 (0) | 2021.10.06 |
---|---|
[파이썬] 11723. 집합 (0) | 2021.10.04 |
[파이썬, Java] 15683. 감시 (0) | 2021.09.30 |
[파이썬, Java] 14888. 연산자 끼워넣기 (0) | 2021.09.28 |
[파이썬, Java] 10974. 모든 순열 (0) | 2021.09.26 |