[백준] 2306번 유전자 (C++)

2026. 3. 19. 01:34·Algorithm/Review
728x90

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

 

 

최근 dp유형이 좀 많이 약한것 같아서 다양한 문제들을 풀면서 dp에 익숙해지기로 하였다.

 

이 문제는 문자열이 주어지고 (a, t), (g, c) 라는 가장 짧은 KOI 유전자가 존재하며

KOI 유전자에 겹쳐서 성립하는 경우도 KOI 유전자라고 하며 이때 최대 길이를 구하는 문제이다.

 

문제 조건에 최대 길이의 KOI 문자열을 구하기 위해서 문자 중간중간을 삭제할 수 있으며 제한은 없다.

 

따라서 내가 해당 문자를 선택하고 버리는 것에 따라서 미래에 영향을 주기 때문에 모든 경우의 수를 구하며 최적화를 해야한다.

 

따라서 dp 테이블을 정의해야한다.

 

스스로 해결: ❌

 

dp[i][j] = i구간부터 j구간까지 KOI 문자열의 최대 길이

로 식을 세우고 점화식을 구성해보았다.

 

최대 길이가 500이므로 N^2, N^3으로도 풀이가 가능하기 때문에

 

큰 문제 : [0, 길이 - 1] 전체 문자열에서 최대 KOI 문자열의 길이

부분 문제 : [l, r] 구간에서 최대 KOI 문자열의 길이

로 생각해서 접근했다.

 

정답코드

#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
typedef long long ll;
#define endl "\n"

struct Tree {
    int Node, left, right;
};

const int INF = 1e9;
const int MAX = 20001;
const int D = 2000000;
const int MOD = 10007;
int dx[] = {0, 0, 1, -1};
int dy[] = {1, -1, 0, 0};
int dp[501][501];

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);

    string s;

    cin >> s;

    int answer = 0;

    for(int l = 1; l <= s.size(); l++) {     // 문자 길이
        for(int i = 0; i + l -1 < s.size(); i++) {     // 시작점
            int j = i + l - 1;      // 끝점

            if((s[i] == 'a' && s[j] == 't') || (s[i] == 'g' && s[j] == 'c')) {
                dp[i][j] = max(dp[i][j], dp[i+1][j-1]+2);
                answer = max(answer, dp[i][j]);
            }

            for(int k = i; k < j; k++) {
                dp[i][j] = max(dp[i][j], dp[i][k] + dp[k+1][j]);
                answer = max(answer ,dp[i][j]);
            }
        }
    }

    cout << answer;
}

 

핵심 코드를 살펴보면

    for(int l = 1; l <= s.size(); l++) {     // 문자 길이
        for(int i = 0; i + l -1 < s.size(); i++) {     // 시작점
            int j = i + l - 1;      // 끝점

            if((s[i] == 'a' && s[j] == 't') || (s[i] == 'g' && s[j] == 'c')) {
                dp[i][j] = max(dp[i][j], dp[i+1][j-1]+2);
                answer = max(answer, dp[i][j]);
            }

            for(int k = i; k < j; k++) {
                dp[i][j] = max(dp[i][j], dp[i][k] + dp[k+1][j]);
                answer = max(answer ,dp[i][j]);
            }
        }
    }

 

문자 길이에 따라 작은 문제를 풀며 큰 문제를 풀 수 있도록 구성하였으며

이때 시작점 i는 0부터 최대길이까지 가능하기 때문에 i + l -1을 해주었다. -1을 한 이유는 아시다시피 인덱스는 0부터 시작하기 때문에 -1을 처리하였다.

 

시작점과 끝점이 같은 경우 시작점에서 한칸 이전 , 도착점에서 한칸 이전 즉 시작점은 가운데에서 왼쪽으로 퍼지고 끝점은 가운데에서 오른쪽으로 퍼지기 때문에 이전값 [i+1][j-1]값을 가져와서 현재 같은 값의 길이 2를 더해주었다.

 

또한 k지점같은 경우 해당 지점 문자를 버릴 수 있기 때문에 시작점에서부터 끝점까지 반복하여 버리는 경우와 왼쪽 구간과 오른쪽 구간을 나눠서 더 큰 값으로 갱신하는 경우를 동시에 찾고 있다.

 

즉 k값이 i 또는 j-1일때 각 지점은 문자를 버리는 것이고 그 외의 지점들은 문자를 나눠서 최대길이를 갱신하는것이다.

 

탑다운 정답코드

#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
typedef long long ll;
#define endl "\n"

struct Tree {
    int Node, left, right;
};

const int INF = 1e9;
const int MAX = 20001;
const int D = 2000000;
const int MOD = 10007;
int dx[] = {0, 0, 1, -1};
int dy[] = {1, -1, 0, 0};
int dp[501][501];
string s;

int func(int l, int r) {
    if(l >= r) return 0;

    int &ret = dp[l][r];

    if(ret != -1) return ret;

    for(int i = l; i < r; i++) {
        ret = max(ret, func(l, i) + func(i+1, r));
    }

    if((s[l] == 'a' && s[r] == 't') || (s[l] == 'g' && s[r] == 'c')) {
        ret = max(ret, func(l+1, r-1) + 2);
    }

    return ret;
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);

    cin >> s;

    memset(dp, -1, sizeof(dp));

    cout << func(0, s.size()-1);
}

 

마찬가지로 나올 수 있는 경우를 쪼개서 구해주었다.

 

 

회고록

dp를 풀기전 무조건 점화식만 생각하지말고 dp라고 감이 잡히는 경우 큰 문제와 부분 문제를 정의할 수 있는지 판별하여

할 수 있는 행동을 나누고 점화식을 세우자

728x90
저작자표시 (새창열림)

'Algorithm > Review' 카테고리의 다른 글

[LeetCode] 3020. Find the Maximum Number of Elements in Subset  (0) 2026.06.28
[LeetCode] 410. Split Array Largest Sum  (0) 2026.06.19
[백준] 1315번 RPG (C++)  (0) 2026.03.15
'Algorithm/Review' 카테고리의 다른 글
  • [LeetCode] 3020. Find the Maximum Number of Elements in Subset
  • [LeetCode] 410. Split Array Largest Sum
  • [백준] 1315번 RPG (C++)
쿨쿨.
쿨쿨.
  • 쿨쿨.
    All of the life
    쿨쿨.
  • 전체
    오늘
    어제
    • 분류 전체보기 (278)
      • Programming (47)
        • C, C++ (18)
        • Python (6)
        • Java (1)
        • HTML,CSS,JS (3)
        • SQL(DB) (13)
        • SpringBoot (3)
        • Android (2)
        • CI,CD (1)
      • Algorithm (173)
        • Review (4)
      • Security (14)
        • WebHacking (3)
        • Websecurity (11)
      • OS (19)
        • Linux (12)
        • Mac os (2)
      • 머신러닝 (1)
      • CS(Computer Science) (12)
        • 컴퓨터 네트워크 (3)
        • 컴퓨터 구조 (1)
        • 인공지능 (8)
      • Docker (1)
      • Dev Book Review (3)
        • Clean Code (1)
        • Effective Java (0)
        • Real MySQL (2)
      • SWM (1)
      • Review (5)
      • AWS (2)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    Bandit
    재귀
    우선순위큐
    DFS
    비트마스킹
    WebSecurity
    에라토스테네스의 체
    wargame
    투포인터
    백트래킹
    트리
    구현
    그리디
    이분탐색
    linux
    누적합
    브루트포스
    깊이우선탐색
    코딩
    Leviathan
    백준
    BFS
    c언어
    그래프 이론
    정렬
    우선순위 큐
    DP
    시뮬레이션
    다이나믹 프로그래밍
    다익스트라
  • 최근 댓글

  • 최근 글

  • 250x250
  • hELLO· Designed By정상우.v4.10.6
쿨쿨.
[백준] 2306번 유전자 (C++)
상단으로

티스토리툴바