티스토리 뷰

[BOJ 2281]데스노트


이 문제는 두 가지 방법으로 풀었는데 내가 처음으로 풀었던 방법은 시간이 더 오래 걸렸다. 그래서 시간이 짧은 다른 풀이로 한번 더 풀었다. 두 풀이 모두 dp를 기초로 하는 풀이기는 하지만 dp테이블의 의미가 달랐다. 내가 처음으로 푼 방식은 구간별로 길이의 합과 그 합을 m에서 뺀 값의 제곱을 미리 다 구해놓았다. 그 값들을 바탕으로 3중 for문을 돌았기 때문에 시간 복잡도는 거의 O(N^3)이라고 할 수 있다. N=1000이기 때문에 거의 터질 수도 있는 풀이지만 온전히 N^3을 도는 것은 아니라서 시간 안에 들어온 것 같다. 이 풀이의 경우 dp[a][b]=c의 의미는 'a부터 b번 째 이름 전까지(표기상으로 '[a,b)')의 합으로 만들 수 있는 최소 공백의 제곱의 합(=c)'이다. 이 풀이의 경우 316ms를 받았다.(1초 제한)



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
#include<stdio.h>
#include<algorithm>
using namespace std;
#define SIZE 1009
#define INF (1<<30)-1
int dp[SIZE][SIZE],sum[SIZE][SIZE];
int arr[SIZE],p_sum[SIZE];
int main(){
    int n,m;    scanf("%d %d",&n,&m);
 
    for(int i=1 ; i<=n ; i++){
        scanf("%d",&arr[i]);
        p_sum[i]=p_sum[i-1]+arr[i];
    }
 
    for(int i=1 ; i<=n+1 ; i++){
        for(int j=1 ; j<=n+1 ; j++){
            dp[i][j]=INF;
        }
    }
 
    for(int i=1 ; i<=n+1 ; i++){
        for(int j=i ; j<=n+1 ; j++){
            if(i==j){
                sum[i][i]=0continue;
            }
            sum[i][j]=p_sum[j-1]-p_sum[i-1];
            sum[i][j]+=(j-i-1);
 
            if(sum[i][j]<=m) //
                dp[i][j]=(m-sum[i][j])*(m-sum[i][j]);
            else dp[i][j]=INF; //
        }
    }
 
    for(int i=1 ; i<=n ; i++){
        for(int j=1;  j+i<=n+1 ; j++){
            for(int k= j ; k<=j+i ; k++){
                dp[j][j+i]=min(dp[j][j+i],dp[j][k]+dp[k][j+i]);
            }
        }
    }
 
    int ans=1<<30;
    for(int i=n ; i>=1 ; i--){
        if(sum[i][n+1]<=m){
            ans=min(ans,dp[1][i]);
        }
    }
    printf("%d\n",ans);
 
    return 0;
}
cs



두 번째 풀이는 재귀함수를 이용한 탑다운 dp이다. 이 풀이의 경우 dp[a][b]=c의 의미는 '현재 b번째 글자를 데스노트 어느 줄의 a번째 칸까지 쓸 때, 남은 글자를 모두 써넣기 위해서 필요한 최소 공백의 제곱의 수'이다. 내가 현재 써야할 이름을 현재 줄에 이어서 쓰는 경우도 있을 수 있고, 다음 줄에 새롭게 쓰는 경우가 있다. 다만 이어서 쓰려면은 반드시 남아 있는 공백의 수가 쓰려는 글자 수보다 크거나 같아야 한다는 조건이 필요하다. 이 풀이의 경우 4ms가 나왔다.



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
#include<stdio.h>
#include<algorithm>
using namespace std;
#define SIZE 1009
int n,m;
int dp[SIZE][SIZE],arr[SIZE];    
int rec(int pos,int cnt){
    if(cnt==n) return 0;
 
    int &ret=dp[pos][cnt];
    if(ret!=-1return ret;
 
    ret=rec(arr[cnt]+1,cnt+1)+(m-pos+1)*(m-pos+1); //다음 줄에 새롭게 씀
    if(pos+arr[cnt]<=m)  //현재 줄에 이어서 씀
        ret=min(ret,rec(pos+arr[cnt]+1,cnt+1));
 
    return ret;
}
int main(){
    scanf("%d %d",&n,&m);
 
    for(int i=0 ; i< n ; i++)
        scanf("%d",&arr[i]);
 
    for(int i=0 ; i<SIZE ; i++){
        for(int j=0 ; j<SIZE ; j++)
            dp[i][j]=-1;
    }
 
    int ans=rec(arr[0]+1,0+1);
    printf("%d\n",ans);
 
    return 0;
}
cs


'Problem Solving > Dynamic Programming' 카테고리의 다른 글

[BOJ 1695] 팰린드롬 만들기  (0) 2019.01.23
[BOJ 1328] 고층 빌딩  (0) 2019.01.22
[BOJ 2568]전깃줄-2  (0) 2019.01.17
[BOJ 2662]기업투자  (0) 2019.01.16
[BOJ]13302-리조트  (0) 2019.01.15
댓글