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

 

17218번: 비밀번호 만들기

첫째 줄과 둘째 줄에 수형이가 눈을 감고 만든 두 문자열이 주어진다. 문자열은 알파벳 대문자로만 이루어져 있으며, 길이는 최대 40자이다. 빈 문자열은 주어지지 않는다. 가장 긴 부분 문자열

www.acmicpc.net

 

 

[난이도] Gold5
[유형] DP

[풀이]
전형적인 LCS (DP) 문제입니다.

 

#include <iostream>
#include <string>
#include <cstring>
#include <algorithm>
using namespace std;
string A,B;
int dp[41][41];
pair<int,int> par[41][41];
int main(){
    cin >> A >> B;
    for(int i=0;i<A.size();i++){
        for(int j=0;j<B.size();j++){
            if(A[i]==B[j]) {
                dp[i+1][j+1] = dp[i][j]+1;
                par[i+1][j+1] = {i,j};
            }else{
                if(dp[i+1][j] > dp[i][j+1]){
                    dp[i+1][j+1] = dp[i+1][j];
                    par[i+1][j+1] = {i+1,j};
                }else{
                    dp[i+1][j+1] = dp[i][j+1];
                    par[i+1][j+1] = {i,j+1};                    
                }
            }
        }
    }
    string ans;
    int i=A.size(),j=B.size();
    while(i!=0&&j!=0){
        if(A[i-1] == B[j-1]) ans+=A[i-1];
        auto v = par[i][j];
        i=v.first;
        j=v.second;
    }
    reverse(ans.begin(),ans.end());
    cout << ans;
}


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

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

 

2253번: 점프

N(2 ≤ N ≤ 10,000)개의 돌들이 같은 간격으로 놓여 있다. 편의상 순서대로 1, 2, …, N번 돌이라고 부르자. 당신은 현재 1번 돌 위에 있는데, 이 돌들 사이에서 점프를 하면서 N번째 돌로 이동을 하려

www.acmicpc.net

 

 

[난이도] Gold4
[유형] DP

[풀이]
DP[n][k] : n = 현재 돌의 번호, k = 이전에 점프한 칸의 개수
k는 최악의 겨우에도
1+2+3+4+...k=10000
이므로 (k+1)k/2 = 10000 의 식을 이용하면 대략 500은 절대 넘지 않으므로
배열을 DP[10000][500] 으로 설정하여 메모리 초과를 피하였씁니다.

 

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int N,M,dp[10001][500],a[10000],inf=9e8;
int sol(int n,int k){
    if(n==N) return 0;
    if(n>N||a[n]) return -1;

    int& ret = dp[n][k];
    if(ret!=-1) return ret;

    ret=inf;
    for(int i=k-1;i<=k+1;i++){
        if(i<1) continue;
        int v = sol(n+i,i);
        if(v==-1) continue;
        ret=min(ret,v);
    }
    if(ret==inf) return ret=-1;
    ret++;
    return ret;
}
int main(){
    scanf("%d%d",&N,&M);
    while(M--){
        int v;
        scanf("%d",&v);
        a[v]=1;
    }
    memset(dp,-1,sizeof(dp));
    int ans = sol(2,1);
    if(ans==-1) puts("-1");
    else printf("%d",ans+1);
}


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

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

 

19623번: 회의실 배정 4

서준이는 아빠로부터 N개의 회의와 하나의 회의실을 선물로 받았다. 각 회의는 시작 시간, 끝나는 시간, 회의 인원이 주어지고 한 회의실에서 동시에 두 개 이상의 회의가 진행될 수 없다. 단,

www.acmicpc.net

 

 

[난이도] Gold3
[유형] DP

[풀이]
l,r,v (시작시간,종료시간,참여인원수)를 저장하는 구조체 P 배열을 선언한 뒤,
시작시간을 기준으로 정렬해줍니다.

그 뒤, lower_bound 사용을 위해 l에 대한 배열 s를 선언하여 따로 저장해줍니다.

DP[i] (sol(i))  : i~N-1 회의가 남았을 때, 회의를 진행할 수 있는 최대 인원

1) 회의 n를 선택 안한 경우
    sol(n+1) (n+1~N-1 회의가 남았을 때, 회의를 진행할 수 있는 최대 인원)

2) 회의 n를 선택한 경우,
    회의 n의 종료 시간보다 시작이 늦는 회의부터 선택할 수 있으므로
    lower_bound(s+n,s+N,a[n].r)-s; 와 같이 lower_bound를 이용하여
    회의 n의 종료시간보다 회의 시작이 늦는 가장 빠른 회의 i를 찾아 주면 됩니다.
    그러면 식은
    (회의 n에 참여하는 사람 수) + sol(i) 이 됩니다.

1,2의 max 값이 sol(i)의 값이 됩니다.

 

 

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
struct P{
    int l,r,v;
};
int N,dp[100001],s[100001];
P a[100001];
bool cmp(P& p,P& q){
    return p.l < q.l;
}
int sol(int n){
    if(n>=N) return 0;
    int& ret = dp[n];
    if(ret!=-1) return ret;
    int i = lower_bound(s+n,s+N,a[n].r)-s;
    ret = max(sol(n+1),a[n].v+sol(i));
    return ret;
}
int main(){
    scanf("%d",&N);
    memset(dp,-1,sizeof(dp));
    for(int i=0;i<N;i++) scanf("%d%d%d",&a[i].l,&a[i].r,&a[i].v);
    sort(a,a+N,cmp);
    for(int i=0;i<N;i++) s[i] = a[i].l;
    printf("%d",sol(0));
}


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

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

 

5569번: 출근 경로

상근이가 사는 도시는 남북 방향으로 도로가 w개, 동서 방향으로 도로가 h개 있다.  남북 방향 도로는 서쪽부터 순서대로 번호가 1, 2, ..., w로 매겨져 있다. 또, 동서 방향 도로는 남쪽부터 순서대

www.acmicpc.net

 

 

[난이도] Gold4
[유형] DP

[풀이]
아래와 같은 정의 4차원 dp를 이용하면 해결이 가능합니다.
이전 상태와, 현재 방향을 각각 f,d에 저장했습니다.
dp[y][x][f][d] : 현재 방향이 d (0:세로, 1:가로) 이고, 이전 이동 상태가 f (0:방향 변경x 1:방향 변경o) 이면서
                 현재 위치가 (y,x)일 때, (h,w)까지 도달하는 경우의 수.

 

 

#include <cstdio>
#include <cstring>
int w,h,dp[101][101][2][2];
int sol(int y,int x,int f,int d){
    if(y>h||x>w) return 0;
    if(y==h&&x==w) return 1;
    int& ret = dp[y][x][f][d];
    if(ret!=-1) return ret;
    if(d==0){
        ret=sol(y+1,x,0,0);
        if(!f) ret = (ret+sol(y,x+1,1,1))%100000;
    }else{
        ret=sol(y,x+1,0,1);
        if(!f) ret = (ret+sol(y+1,x,1,0))%100000;
    }
    return ret;
}
int main(){
    scanf("%d%d",&w,&h);
    memset(dp,-1,sizeof(dp));
    printf("%d",(sol(2,1,0,0)+sol(1,2,0,1))%100000);
}


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

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

 

2302번: 극장 좌석

주어진 조건을 만족하면서 사람들이 좌석에 앉을 수 있는 방법의 가짓수를 출력한다. 방법의 가짓수는 2,000,000,000을 넘지 않는다. (2,000,000,000 < 231-1)

www.acmicpc.net

 

 

[난이도] Silver1
[유형] DP

[풀이]
자리를 바꿀 때는 인접한 두 위치의 자리만 바꿀 수 있습니다.
예를 들어 1 2 3 이 있을 때, 세 숫자 모두 자리가 바뀌려면 3 1 2 가 되어야 하는데
3이 원래 위치에서 2만큼 떨어지게 되므로 조건을 만족하지 못하게 됩니다.

그러므로 결국 VIP석이 없는 연속된 자리가 M개 있을 때, 앉을 수 있는 경우의 수는
인접한 두 자리의 쌍을 고르는 경우의 수가 됩니다.

경우의 수가 너무 많으므로 다이나믹 프로그래밍을 이용해야 합니다.

DP[i] : 를 i개의 연속된 자리에서 앉을 수 있는 경우의 수
위와 같이 정의 했을 때,
i) 1번째 자리를 바꾸지 않은 경우
1번째 자리를 제외하고 나머지 i-1개의 자리에 대해서 DP[i-1]을 구해줘야 합니다.

ii) 1,2번째 자리를 바꾼 경우
1,2번째 자리를 제외하고 나머지 i-2개의 자리에 대해서 DP[i-2]를 구해줘야 합니다.

i,ii 두 경우의 수를 더한 것이 DP[i] 이므로
점화식은 DP[i] = DP[i-1] + DP[i-2] 인 것을 알 수 있습니다.

DP[1]~DP[N]을 미리 구해놓고
VIP 좌석간 사이에 연속된 좌석에 앉을 수 있는 경우의 수는 위의 DP 값들로 구하면 됩니다.
각 구간의 값들 곱해주면 총 경우의 수가 됩니다.

 

#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
int N,M,dp[41],ans=1;
int sol(int n){
    if(n<=1) return 1;
    int& ret = dp[n];
    if(ret!=-1) return ret;
    ret=sol(n-1)+sol(n-2);
    return ret;
}
int main(){
    vector<int> v;
    scanf("%d%d",&N,&M);
    v.push_back(0);
    for(int i=0;i<M;i++) {
        int t;
        scanf("%d",&t);
        v.push_back(t);
    }
    v.push_back(N+1);
    memset(dp,-1,sizeof(dp));
    sol(N);
    for(int i=1;i<v.size();i++){
        int a = sol(v[i]-v[i-1]-1);
        if(a>0) ans*=a;
    }
    printf("%d",ans);
}


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



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

 

21757번: 나누기

$N$개의 정수 수열 $A_1, A_2, \dots , A_N$이 주어진다. 수열을 각각이 연속된 네 부분으로 나누려고 한다. 단, 각 부분은 최소 하나의 수를 포함해야 한다. 또, 각 부분의 합은 모두 같아야 한다. 즉, 어

www.acmicpc.net

 

 

[난이도] Gold3
[유형] DP

[풀이]
나중에..

 

#include <cstdio>
#include <cstring>
using ll = long long;
using namespace std;
int N,sum[100001],base,cnt;
ll dp[5];
int main(){
    scanf("%d",&N);
    for(int i=1;i<=N;i++) {
        int v;
        scanf("%d",&v);
        sum[i]=sum[i-1]+v;
        if(sum[i]==0) cnt++;
    }
    if(sum[N]==0){
        if(cnt<4) puts("0");
        else printf("%lld",(ll)(cnt-1)*(cnt-2)*(cnt-3)/6);
        return 0;
    }
    if(sum[N]%4){
        puts("0");
        return 0;
    }
    base=sum[N]/4;

    dp[0]=1;
    for(int i=1;i<N;i++){
        if(sum[i]%base) continue;
        int j=sum[i]/base;
        if(j==0 || j>3) continue;
        dp[j]+=dp[j-1];
    }
    printf("%lld",dp[3]);
}


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

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

 

17623번: 괄호

6개의 문자 ‘(’, ‘)’, ‘{’, ‘}’, ‘[’, ‘]’ 로 이루어진 올바른 괄호 문자열 W에 대하여 정수의 ‘괄호값’을 지정하는 함수 val(W)가 정의되어 있다. 먼저 올바른 괄호 문자열부터 정의해

www.acmicpc.net

 

 

[난이도] Gold2
[유형] DP

[풀이]
val[i] : val(X) = N을 만족하는 올바른 괄호 문자열 중에서 dmap(X) 값이 최소인 X

val[i]가 될 수 있는 후보 문자열은
괄호를 감싸는 연산으로 만든
"1" + val[i/2] + "2" (소괄호)
"3" + val[i/3] + "4" (중괄호)
"5" + val[i/5] + "6" (대괄호)

concatenation 으로 만든
val[i-j]+val[j] (1<= j < i) 가 있습니다.

이들 중 최솟값을 val[i]로 업데이트 해주면 되는데
최솟값을 정수로 비교하려면 정수 범위를 넘어가버려서 비교가 불가합니다..
그러므로 string 상태에서 비교를 해야합니다.
string 숫자 비교시
12221 보다 344가 크다고 판단하기 때문에
우선 숫자 string의 크기부터 비교해주어야 합니다.

 

#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
int tc,N;
string val[1001],cvt="0(){}[]";
string check(string a,string b){
    if(b=="-1") return a;
    if(a.size() < b.size()) return a;
    if(a.size() > b.size()) return b;
    return min(a,b);
}
void sol(){
    for(int i=1;i<=1000;i++) val[i]="-1";
    val[1]="12";
    val[2]="34";
    val[3]="56";
    for(int i=4;i<=1000;i++){
        string s;
        if(i%2==0) val[i]=check("1"+val[i/2]+"2",val[i]);
        if(i%3==0) val[i]=check("3"+val[i/3]+"4",val[i]);
        if(i%5==0) val[i]=check("5"+val[i/5]+"6",val[i]);
        for(int j=1;j<i;j++) {
            val[i] = check(val[i-j]+val[j],val[i]);
        }
    }
}
int main(){
    cin >> tc;
    sol();
    while(tc--){
        cin >> N;
        for(auto c : val[N]) {
            cout << cvt[c-'0'];
        }
        cout <<'\n';
    }
}


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

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

 

1106번: 호텔

첫째 줄에 C와 형택이가 홍보할 수 있는 도시의 개수 N이 주어진다. C는 1,000보다 작거나 같은 자연수이고, N은 20보다 작거나 같은 자연수이다. 둘째 줄부터 N개의 줄에는 각 도시에서 홍보할 때

www.acmicpc.net

 

 

[난이도] Gold5
[유형] DP

[풀이]
DP[c] : 고객을 적어도 c명 늘이기 위해 투자해야하는 최솟값
배낭문제 응용입니다.
0~N-1까지 반복문을 돌려주면서 i번 도시까지 홍보할때의 최솟값을 DP 배열에 순차적으로 저장해주면 됩니다.

 

 

#include <cstdio>
#include <algorithm>
using namespace std;
int C,N,p[20],cost[20],dp[1001],inf=9e8;
int main(){
    scanf("%d%d",&C,&N);
    for(int i=0;i<N;i++) scanf("%d%d",&cost[i],&p[i]);
    for(int i=1;i<=C;i++) dp[i]=inf;
    for(int i=0;i<N;i++){
        for(int j=1;j<=C;j++){
            for(int k=1;;k++){
                int v=0;
                if(j-p[i]*k>=0) v=dp[j-p[i]*k];
                if(dp[j] > v+cost[i]*k) dp[j] = v+cost[i]*k;
                else break;
            }
        }
    }
    printf("%d",dp[C]);
}


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

+ Recent posts