ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 백준 1053(팰린드롬 공장)
    전공/알고리즘 2020. 10. 22. 13:09

    www.acmicpc.net/problem/1053

     

    1053번: 팰린드롬 공장

    팰린드롬이란, 앞에서부터 읽었을 때와, 뒤에서부터 읽었을 때가 같은 문자열이다. 모든 문자열이 팰린드롬이 아니기 때문에 다음과 같은 4가지 연산으로 보통 문자열을 팰린드롬으로 만든다.

    www.acmicpc.net

    진짜 대단하다 컴퓨터는 

    이 문제를 풀면서 컴퓨터는 진짜로 대단하다는 걸 느낀다. 사람이라면 조건을 다 따져서 하드코딩해야 하는 것을 컴퓨터는 그냥 다 해보면 된다. 

     

    그리고 dp는 더 아름답다.

     

    이 문제는 dp이다. 태그에서 찾았기 때문에 dp인걸 알았지만 어떻게 풀어야 할지 감이 안 왔었다. 

     

    그러다가 그냥 하면 되지 않나 생각이 드는 순간 감이 왔다. 

     

    일단 우리는 팰린드롬인지 아닌지를 생각할 필요가 별로 없다. 

    이게 제일 중요한 핵심인듯하다. 

    할 수 있는 모든 것을 하면 된다. 

     

    처음에 제일 중요한 생각은 4.의 과정인 단 한 번밖에 못 쓰는 특수케이스를 먼저 모든 경우에 대해서 처리해주겠다는 생각이 들어야 한다. 

     

    50개의 문자열에서 서로다른 두개를 뽑아 봤자 n^2을 넘지 않는다. 

    근데 이제 의문이 생길 수 있다. 

     

    이걸 먼저하면 안되지 않을까 

     

    하지만 이것은 좀 자명하다 교환을 한다는 것은 예시처럼 babacvabba 아다리가 맞아야 다른 연산을 하는 것보다 연산이 한 번 줄게 된다. 이거에 대해선 더 얘기하지 말자. 

     

    가장 중요한 dp[][]는 이중배열이다. dp[i][j]에는 i,j까지 팰린드롬으로 만드는데 드는 최소비용을 저장한다.

     

    그러고 이제 할 수 있는 연산

    삽입, 삭제, 교환을 생각해보자

     

    이것을 먼저 생각하자는 이유는 우리는 삽입,삭제,교환을 연산을 하고나서 다음 index로 진행을 할 것인데 이 연산이 어떤 연산인가에 따라서 index조정해야하기 때문이다. 

     

    삽입을 하는 것을 잘 생각해보자 abaa에서 왼쪽에 a를 삽입하나 오른쪽 a를 삭제하나 똑같은 결과를 가져온다. 

     

    따라서 삽입과 삭제는 같은 연산으로 봐도 좋다.

    교환은 bcaqec

    에서 b,c 중 하나를 교환하고 나면 당연히 c와 e로 이동한다. 

     

     

    dp[l][r]에서 

    왼쪽 삽입 오른쪽 삭제는 dp[l][r-1] 로 이동하고, 오른쪽 삽입 왼쪽 삭제는 dp[l+1][r]로 이동, 

    교환은 dp[l+1][r-1]로 이동한다. 

     

    따라서 dp[l][r] = min(dp[l][r-1], dp[l+1][r], dp[l+1][r-1])+1 이라고 할 수 있는데

     

    교환할 때를 생각해보면 만약 두 수가 같으면 비용이 발생하지 않는다. 

     

    따라서 

     

    dp[l][r] = min(dp[l][r-1]+1, dp[l+1][r]+1, dp[l+1][r-1])+(s[l]!=s[r]))

    이 된다. 

     

    다시한번 얘기하지만 우리는 가능성만 생각하면 된다. 굳이 그리디적인 사고과정을 복잡하게 할 필요가 없는 것 같다. 

     

    #include<iostream>
    #include<string>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    #define MAX 55
    #define INF 987654321
    
    int dp[MAX][MAX];
    string s;
    
    int solve(int l, int r, string a) {
    	if (dp[l][r] != -1)return dp[l][r];
    	if (l >= r) return 0;
    	int &ret = dp[l][r];
    	ret = min({ solve(l + 1, r, a) + 1, solve(l, r - 1, a) + 1, solve(l + 1, r - 1, a) + (a[l] != a[r]) });
    	
    	return ret;
    }
    
    int main() {
    	cin >> s;
    	memset(dp, -1, sizeof(dp));
    	int ans = solve(0, s.size() - 1, s);
    	for (int i = 0; i < s.size(); i++) {
    		for (int j = i+1; j < s.size(); j++) {
    			memset(dp, -1, sizeof(dp));
    			string tmp = s;
    			swap(tmp[i], tmp[j]);
    			ans = min(ans, solve(0, s.size() - 1, tmp) + 1);
    		}
    	}
    	cout << ans;
    
    }

     

     

     

    '전공 > 알고리즘' 카테고리의 다른 글

    백준 15487(A[j]-A[i]+A[l]-A[k])  (0) 2020.10.23
    백준 11062(카드게임)  (0) 2020.10.22
    백준 19576(약수)  (0) 2020.10.17
    백준 19591(독특한 계산기)  (0) 2020.10.14
    백준 20047(동전 옮기기)  (0) 2020.10.14

    댓글

Designed by Tistory.