포함배제 원리를 이용하는 dp문제 2개를 풀어보자.
1. BOJ 14517
https://www.acmicpc.net/problem/14517
14517번: 팰린드롬 개수 구하기 (Large)
팰린드롬(palindrome)이란 앞에서부터 읽으나 뒤에서부터 읽으나 같은 단어를 말한다. 'aba'나 'a'와 같은 단어는 팰린드롬이며, 'abaccbcb'나 'anavolimilana'와 같은 단어는 팰린드롬이 아니다. 승수는 주
www.acmicpc.net
[ 풀이 ]
문제를 잘 이해해야 한다. 부분수열이므로 연속된 팰린드롬일 필요가 없다.
맨처음과 끝에서 시작해서 좁혀가면서 갱신해주면 된다.
dp[s][e]:=구간 [s,e]를 볼 때 팰린드롬의 총 개수라고 정의하면, 상태전이는 3가지가 있다.
1. s를 사용안하고 [s+1,e]로 전이
2. e를 사용안하고 [s, e-1]로 전이
3. s, e를 사용안하고 [s+1, e-1]로 전이
4. s와 e 문자가 같다면 둘다 사용하고 [s+1,e-1]로 전이
이 때, 1번과 2번을 세면서 3번이 중복해서 세짐을 알 수 있다.
따라서 1번 빼준다는게 이 점화식의 핵심이다.
[ Code ]
#include <bits/stdc++.h>
using namespace std;
int dp[1001][1001], mod = 10007;
string str;
// dp[s][e]: [s, e] #palindrome
int go(int s, int e) {
if (s > e) return 0; // base
if (s == e) return 1; // base
int& ret = dp[s][e];
if (~ret) return ret;
// CASE1 + CASE2 - CASE3
ret = (go(s + 1, e) + go(s, e - 1) - go(s + 1, e - 1) + mod) % mod;
// if palindrome..
if (str[s] == str[e]) ret = (ret + 1 + go(s + 1, e - 1)) % mod;
return ret;
}
int main() {
ios::sync_with_stdio(0); cin.tie(0);
cin >> str;
memset(dp, -1, sizeof(dp));
// answer
cout << go(0, str.length() - 1);
}
2. BOJ 14165
https://www.acmicpc.net/problem/14165
14165번: Team Building
Every year, Farmer John brings his N cows to compete for "best in show" at the state fair. His arch-rival, Farmer Paul, brings his M cows to compete as well (1≤N≤1000,1≤M≤1000). Each of the N+M cows at the event receive an individual integer score.
www.acmicpc.net
[ 풀이 ]
크기순으로 매칭할꺼고 순서는 상관없으므로 정렬해놓고 시작하자.
dp를 어떻게 만들건지는 쉽다. FJ와 FP가 모두 몇번 인덱스까지 봤을 때 길이가 얼마인지 대충 이정도 정보가 필요하다.
dp[i][j][k]:= FJ가 i번을 볼 것이고, FP가 j번을 볼 것이고 현재 만들어진 길이가 k가 되는 경우의 수라고 하자.
식 세울때 미세한 팁이 있다면, 전이시키는 점화식의 특징은 항상 현재값을 그냥 안쓰고 다음으로 넘겨주는게
대부분 들어가게 된다.
전이는 4가지가 있다.
1. i를 사용안하고 i+1, j, k로 전이
2. j를 사용안하고 i, j+1, k로 전이
3. i,j를 사용안하고 i+1, j+1, k로 전이
4. i,j를 사용하고 i+1, j+1, k+1로 전이
여기서 1,2를 셀 때 3은 중복해서 세진다. 마찬가지로 1번 빼주고 점화식을 완성시켜주면 된다.
[ Code ]
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
ll n, m, k, a[1001], b[1001], dp[1001][1001][11];
const ll mod = 1e9 + 9;
ll go(int i, int j, int len) {
if (len == k) return 1; // base case
if (i == n + 1 || j == m + 1) return len == k; // 1개가 먼저 끝났을 때
ll& ret = dp[i][j][len];
if (~ret) return ret;
// CASE1+CASE2-CASE3
ret = (go(i + 1, j, len) + go(i, j + 1, len) - go(i + 1, j + 1, len) + mod) % mod;
// CASE4
if (a[i] > b[j]) ret = (ret + go(i + 1, j + 1, len + 1)) % mod;
return ret;
}
int main() {
ios::sync_with_stdio(0); cin.tie(0);
cin >> n >> m >> k;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= m; i++) cin >> b[i];
sort(a + 1, a + n + 1);
sort(b + 1, b + m + 1);
memset(dp, -1, sizeof(dp));
cout << go(1, 1, 0) << '\n';
}
'PS > BOJ' 카테고리의 다른 글
백준 15561 / C++ / 구간합 최댓값 쿼리 O(logN)에 처리하기 (0) | 2022.08.01 |
---|---|
백준 15560 / C++ (0) | 2022.08.01 |
백준 11066 / C++ (0) | 2022.07.21 |
백준 6062 / C++ (0) | 2022.07.21 |
백준 1520 / C++ (0) | 2022.07.21 |
댓글