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

 

2671번: 잠수함식별

입력에 들어있는 스트링을 읽고, 이것이 잠수함의 엔진소리를 나타내는 스트링인지 아니면 그냥 물속의 잡음인지를 판정한 후, 잠수함의 엔진 소리에 해당하는 스트링이면 "SUBMARINE"을 출력하고

www.acmicpc.net

 

 

[난이도] Gold5
[유형] DP

[풀이]
정규표현식을 이용하면 간단히 해결할 수 있지만 평소 사용하지 않던 문법이라
다이나믹 프로그래밍을 이용하여 해결했습니다.

top-down DP를 이용하였습니다
dp[n] : 부분 문자열 s[n..s.size()-1] 이 SUBMARINE이면 1, NOISE이면 0을 return

s[n..n+1] 이 "01"인 경우와
s[n..n+2] 이 "100"인 경우로 나누어서 해결해주면 됩니다.

"01"인 경우 한가지 케이스밖에 없으므로 sol(n+2)를 호출하여 0인지 1인지 확인해주면 됩니다.

"100"인 경우 10001...,100001... 와 같은 케이스가 가능하므로 1이 언제 처음 등장하는지 체크해 준 뒤
만약 '1'이 i부터 등장한다면 i부터 1이 등장할 때까지 sol(i+1),sol(i+2),sol(i+3)...을 '0'이 등장하기 전까지
계속 호출해주며 return 값이 1이 나온다면 정답이므로 종료해줍니다.

 

#include <cstdio>
#include <cstring>
#include <iostream>
#include <string>
using namespace std;
string s;
int dp[151];
int sol(int n){
    if(n==s.size()) return 1;
    int& ret = dp[n];
    if(ret!=-1) return ret;

    if(n>=s.size()-1) return ret=0;
    string t = s.substr(n,2);

    if(t=="01" && sol(n+2)) return ret=1;
    if(n>=s.size()-3) return ret=0;
    t = s.substr(n,3);

    if(t=="100"){
        int i;
        for(i=n+3;i<s.size();i++){
            if(s[i]=='1') break;
        }
        for(int j=i;j<s.size();j++){
            if(s[j]!='1') break;
            else if(sol(j+1)) return ret=1;
        }
    }
    return ret=0;
}
int main(){
    cin >> s;
    memset(dp,-1,sizeof(dp));
    if(sol(0)) cout << "SUBMARINE";
    else cout << "NOISE";
}


https://github.com/has2/Problem-Solving/blob/master/boj-solved.ac/Gold5/2671.cpp

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

 

14728번: 벼락치기

ChAOS(Chung-ang Algorithm Organization and Study) 회장이 되어 일이 많아진 준석이는 시험기간에도 일 때문에 공부를 하지 못하다가 시험 전 날이 되어버리고 말았다. 다행히도 친절하신 교수님께서 아래와

www.acmicpc.net

 

 

[난이도] Gold5
[유형] DP

[풀이]
전형적인 배낭문제입니다.

 

#include <cstdio>
#include <algorithm>
using namespace std;
int N,T,dp[10001];
pair<int,int> p[100];
int main(){
    scanf("%d%d",&N,&T);
    for(int i=0;i<N;i++) scanf("%d%d",&p[i].first,&p[i].second);
    for(int i=0;i<N;i++){
        for(int j=T;j>=0;j--){
            if(j-p[i].first>=0) dp[j] = max(dp[j],dp[j-p[i].first]+p[i].second);
        }
    }
    int ans=0;
    for(int i=1;i<=T;i++) ans=max(ans,dp[i]);
    printf("%d",ans);
}


https://github.com/has2/Problem-Solving/blob/master/boj-solved.ac/Gold5/14728.cpp

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

 

2591번: 숫자카드

1부터 34까지 수가 적힌 카드가 충분히 많이 있다. 이들 중 몇 장을 일렬로 늘어놓고, 그 숫자를 차례로 적었다. 예를 들어 아래와 같이 카드가 놓인 경우 숫자를 차례로 적으면 27123이 된다. 나중

www.acmicpc.net

 

 

[난이도] Gold5
[유형] DP

[풀이]
top-down dp를 이용해 해결할 수 있습니다.
dp[n] : s[n..N-1] 을 만들 수 있는 경우의 수.

1자리수를 선택하는 경우와 2자리수를 선택하는 경우를 나누어서 dp[n] 에 더해주면 됩니다.
두자리수 s[n..n+1]이 35보다 적은 경우 dp[n+2]를 더해줄 수 있습니다.
s[n]이 '0'인 경우는 예외처리를 해주어야 합니다.

 

#include <iostream>
#include <string>
#include <cstring>
using namespace std;
using ll = long long;
int N;
ll dp[41];
string s;
ll sol(int n){
    if(n==N) return 1;
    ll& ret = dp[n];
    if(ret!=-1) return ret;
    ret=0;
    if(s[n]=='0') return 0;
    if(n<N-1) {
        string a = s.substr(n,2);
        if(stoi(a)<35){
            ret+=sol(n+2);
            if(a[1]=='0') return ret;
        }
    }
    ret+=sol(n+1);
    return ret;
}
int main(){
    cin >> s;
    N = s.size();
    memset(dp,-1,sizeof(dp));
    printf("%lld",sol(0));
}


https://github.com/has2/Problem-Solving/blob/master/boj-solved.ac/Gold5/2591.cpp

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

 

14852번: 타일 채우기 3

첫째 줄에 경우의 수를 1,000,000,007로 나눈 나머지를 출력한다.

www.acmicpc.net

 

 

[난이도] Gold5
[유형] DP

[풀이]

DP[i][0] : 2xi 개의 칸이 전부 차있는 타일 조합의 수
         예시(i=4)   ....
                     ....
DP[i][1] : (1,i) 칸을 제외한 2xi-1 개의 칸이 모두 차있는 타일 조합의 수
         예시(i=4)   ...
                     ....
DP[i][2] : (2,i) 칸을 제외한 2xi-1 개의 칸이 모두 차있는 타일 조합의 수
         예시(i=4)   ....
                     ...

점화식 및 생성 원리는 다음과 같습니다.
dp[i][0]=(2*dp[i-1][0]+dp[i-1][1]+dp[i-1][2]+dp[i-2][0])%m;
    => 0번 모양의 2x(i-1) 타일에서 2x1 타일 1개 또는 1x1 타일 2개를 더해 0번 모양의 2xi 타일을 만들 수 있으므로
       2*dp[i-1][0]
    => 1,2번 모양의 2x(i-1) 타일에서 1x2 타일 1개와 1x1 타일 1개를 더해 0번 모양의 2xi 타일을 만들 수 있으므로
       dp[i-1][1]+dp[i-1][2]
    => 0번 모양의 2x(i-2) 타일에서 1x2 타일 2개를 더해 0번 모양의 2xi 타일을 만들 수 있으므로
       dp[i-2][0]

dp[i][1]=dp[i-1][0]+dp[i-1][2];
    => 0번 모양의 2x(i-1) 타일에서 1x1 타일 1개를 더해 1번 모양의 2xi 타일을 만들 수 있으므로
       dp[i-1][0]
    => 2번 모양의 2x(i-1) 타일에서 1x2 타일 1개를 더해 1번 모양의 2xi 타일을 만들 수 있으므로
       dp[i-1][2]

dp[i][2]=dp[i-1][0]+dp[i-1][1];
    => 0번 모양의 2x(i-1) 타일에서 1x1 타일 1개를 더해 2번 모양의 2xi 타일을 만들 수 있으므로
       dp[i-1][0]
    => 1번 모양의 2x(i-1) 타일에서 1x2 타일 1개를 더해 2번 모양의 2xi 타일을 만들 수 있으므로
       dp[i-1][2]

최종 답은 직사각형 모양의 2xN 타일의 개수이므로 dp[N][0]을 출력해주면 됩니다.

 

#include <cstdio>
using ll = long long;
int N;
ll dp[1000001][3],m=1000000007;
int main(){
    scanf("%d",&N);
    dp[1][0]=2;
    dp[0][0]=dp[1][1]=dp[1][2]=1;
    for(int i=2;i<=N;i++){
        dp[i][0]=(2*dp[i-1][0]+dp[i-1][1]+dp[i-1][2]+dp[i-2][0])%m;
        dp[i][1]=dp[i-1][0]+dp[i-1][2];
        dp[i][2]=dp[i-1][0]+dp[i-1][1];
    }
    printf("%lld",dp[N][0]);
}


https://github.com/has2/Problem-Solving/blob/master/boj-solved.ac/Gold5/14852.cpp

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

 

2240번: 자두나무

자두는 자두를 좋아한다. 그래서 집에 자두나무를 심어두고, 여기서 열리는 자두를 먹고는 한다. 하지만 자두는 키가 작아서 자두를 따먹지는 못하고, 자두가 떨어질 때까지 기다린 다음에 떨어

www.acmicpc.net

 

 

[난이도] Gold5
[유형] DP

[풀이]
DP[t][w][k] : 현재 t초이며 w만큼 움직였고 k 위치에 있을 때 얻을 수 있는 최대 자두 개수.

 

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int T,W,a[1001],dp[1001][31][2];
int sol(int t,int w,int k){
    if(t>T) return 0;
    int& ret = dp[t][w][k];
    if(ret!=-1) return ret;
    ret = (k==a[t]) + sol(t+1,w,k);
    if(w<W) ret = max(ret,(k!=a[t]) + sol(t+1,w+1,!k));
    return ret;
}
int main(){
    scanf("%d%d",&T,&W);
    for(int i=1;i<=T;i++) {
        scanf("%d",&a[i]);
        a[i]--;
    }
    memset(dp,-1,sizeof(dp));
    printf("%d",sol(1,0,0));
}


https://github.com/has2/Problem-Solving/blob/master/boj-solved.ac/Gold5/2240.cpp

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

 

2229번: 조 짜기

알고스팟 캠프에 N(1 ≤ N ≤ 1,000)명의 학생들이 참여하였다. 학생들은 열심히 공부를 하고 있었는데, 어느 날 조별 수업을 진행하기로 하였다. 조별 수업의 목적은 잘 하는 학생들과 덜 잘 하는

www.acmicpc.net

 

 

[난이도] Gold5
[유형] DP

[풀이]
다이나믹 프로그래밍을 이용해서 해결 할 수 있습니다.

DP[i] : i번 학생까지 조에 포함시켰을 때, 조가 잘 짜여진 정도의 최댓값

위와 같이 정의하면 점화식은 다음과 같습니다.
DP[i] = DP[j] + maxv:(1~j 중 최댓값) - minv:(1~j 중 최솟값) (0<=j<=i-1)

i번 학생을 1~i 번 학생이 있는 조, 2~i번 학생이 있는 조, ... i~i번 학생이 있는 조 (조에 i번 혼자인 경우)
에 포함시켜 보면서 그 중 최댓값을 DP[i]로 업데이트 하는 방식입니다.

 

#include <cstdio>
#include <algorithm>
using namespace std;
int N,dp[1001],a[1001];
int main(){
    scanf("%d",&N);
    for(int i=1;i<=N;i++) scanf("%d",&a[i]);
    for(int i=2;i<=N;i++){
        int minv=a[i],maxv=a[i];
        for(int j=i-1;j>=0;j--){
            maxv=max(a[j+1],maxv);
            minv=min(a[j+1],minv);
            dp[i] = max(dp[i],dp[j]+maxv-minv);
        }
    }
    printf("%d",dp[N]);
}


https://github.com/has2/Problem-Solving/blob/master/boj-solved.ac/Gold5/2229.cpp

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

 

1720번: 타일 코드

2×N 크기의 넓은 판을 1×2 (또는 2×1) 크기와 2×2 크기의 타일로 채우려고 한다. 여러 가지 경우가 있을 수 있으므로, 각각을 하나의 코드로 대응시켜서 암호화에 이용하려고 한다. 그런데 문제가

www.acmicpc.net

 

 

[난이도] Gold5
[유형] DP

[풀이]
DP[i] = DP[i-1] + DP[i-2]*2 점화식을 이용해 모든 코드의 개수를 구할 수 있습니다.

이미 좌우 대칭인 코드를 제외한 모든 코드는 자신과 대칭이 되는 코드를 가지고 있습니다.
이미 좌우 대칭인 코드의 개수를 t라고 했을 때,
최종 정답은 (DP[N]-t)/2 + t 가 됩니다.
그 이유는 DP[N]-t 가 자신과 대칭이 되는 코드를 가지고 있는 코드들의 개수이므로
중복 제거를 위해 이것을 2로 나누어 주면 중복이 제거되기 때문입니다.
여기에 다시 좌우 대칭인 코드의 개수인 t를 더해줌으로써 최종 답이 됩니다.

t를 구하는 방법은 홀수와 짝수인 경우가 달라집니다.
홀수인 경우 좌우 대칭이려면 가운데 무조건 2x1 타일이 있어야 하므로
t = DP[N/2] 가 됩니다.

짝수인 경우에는 중앙에 2x1 타일 두개, 2x2 타일 한개가 위치하고, 이를 중심으로 대칭인 경우도
추가해 주어야 하므로
t = DP[N/2] + 2*DP[N/2-1] 이 됩니다.

 

#include <cstdio>
int N,dp[31];
int main(){
    scanf("%d",&N);
    dp[1]=dp[0]=1;
    for(int i=2;i<=N;i++) dp[i]=dp[i-1]+dp[i-2]*2;
    int t=dp[N/2];
    if(N%2==0) t+=2*dp[N/2-1];
    printf("%d",(dp[N]-t)/2+t);
}


https://github.com/has2/Problem-Solving/blob/master/boj-solved.ac/Gold5/1720.cpp

 

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

 

14925번: 목장 건설하기

랜드 씨는 퇴직금으로 땅을 사서 목장을 지으려 한다.  그가 사려고 소개받은 땅은 직사각형이고 대부분 들판이지만, 여기저기에 베기 어려운 나무와 치울 수 없는 바위가 있다. 그는 목장을 하

www.acmicpc.net

 

[난이도] Gold4
[유형] DP

[풀이]
DP를 이용해 O(N*M)에 해결이 가능합니다.
dp[i][j] : (i,j) 가 오른쪽 아래 모서이리 때 만들 수 있는 정사각형의 변의 최대 길이.
점화식을 위와 같이 정의했을 때, dp[i][j]가 최소 1이 되려면 map[i][j]==0 이어야 합니다.
이제 미리 구해져 있는 dp[i-1][j-1],dp[i][j-1],dp[i-1][j] 값들을 이용해 최대 길이를 구할 수 있습니다.
위 세 개의 값을 1개라도 0이면 1보다 큰 정사각형을 만들 수 없기 때문에 예외처리 해줍니다.

그 후엔 아래와 같은 점화식으로 세 값중 가장 작은 값에 1을 더한 값이 dp[i][j]가 됩니다.
dp[i][j]=min(min(dp[i-1][j-1],dp[i][j-1]),dp[i-1][j])+1;

그림을 그려보시면 쉽게 파악이 되실 겁니다.

 

#include <cstdio>
#include <algorithm>
using namespace std;
int M,N,map[1001][1001],dp[1001][1001];
int main(){
    scanf("%d%d",&M,&N);
    for(int i=1;i<=M;i++)
        for(int j=1;j<=N;j++) scanf("%d",&map[i][j]);
    int ans=0;
    for(int i=1;i<=M;i++){
        for(int j=1;j<=N;j++){
            if(!map[i][j]){
                if(!dp[i-1][j-1] || !dp[i][j-1]|| !dp[i-1][j]) dp[i][j]=1;
                else dp[i][j]=min(min(dp[i-1][j-1],dp[i][j-1]),dp[i-1][j])+1;
                ans=max(ans,dp[i][j]);
            }
        }
    }
    printf("%d",ans);
}


https://github.com/has2/Problem-Solving/blob/master/boj-solved.ac/Gold4/14925.cpp

+ Recent posts