Codeforces Round #556 (Div. 2) - D. Three Religions(动态规划)
Problem Codeforces Round #556 (Div. 2) - D. Three Religions
Time Limit: 3000 mSec
Problem Description
Input
Output
Sample Input
6 8
abdabc
+ 1 a
+ 1 d
+ 2 b
+ 2 c
+ 3 a
+ 3 b
+ 1 c
- 2
Sample Output
YES
YES
YES
YES
YES
YES
NO
YES
题解:动态规划,意识到这个题是动态规划之后难点在于要优化什么东西,本题是让判断原串能否划分成题中不断更新的三个字符串,通常情况下dp数组不仅仅记录true/false这种信息,因为这种信息往往可以在不改变复杂度的情况下通过记录更具体的信息来直接导出,而这些更具体的信息会给状态的转移带来便利,本题就是这样的情况。
意识到本题的dp属于分段决策同样很重要,对于当前的三个字符串,判断是否合法的方式是逐个加入字符,逐个加入的过程就是天然的阶段,而每个阶段需要做出的决策是加入哪一个字符串的字符,在这个过程中维护的信息就是把第一个串的前 a 个字符,第二个串的前 b 个字符,第三个串的前 c 个字符放进去所需要原串的最小长度。
有了这样的状态定义转移方程自然很简单,比如考虑dp[a][b][c],并且是从dp[a-1][b][c]转移过来的,那么dp[a][b][c]就是在dp[a-1][b][c]位置之后第一次出现第一个串第a个字符的位置,为了能够O(1)转移,预处理出对于原串的每个位置i,对每个小写英文字母x,i及i以后第一次出现x的位置,这很容易在O(26 * n)的复杂度内解决。这样每次状态转移只需要常数时间,正常情况下总的复杂度是O(q * 250^3),这肯定会T,但是考虑到每次新加入一个字符需要重新计算的dp值只有250^2个,因此复杂度实际为O(q * 250^2),可以接受。
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 #define REP(i, n) for (int i = 1; i <= (n); i++) 6 #define sqr(x) ((x) * (x)) 7 8 const int maxn = 100000 + 100; 9 const int maxm = 200000 + 100; 10 const int maxs = 256; 11 12 typedef long long LL; 13 typedef pair<int, int> pii; 14 typedef pair<double, double> pdd; 15 16 const LL unit = 1LL; 17 const int INF = 0x3f3f3f3f; 18 const double eps = 1e-14; 19 const double inf = 1e15; 20 const double pi = acos(-1.0); 21 const int SIZE = 100 + 5; 22 const LL MOD = 1000000007; 23 24 int n, q; 25 int type; 26 int Next[maxn][26]; 27 int dp[maxs][maxs][maxs]; 28 string str, opt, word; 29 string ss[3]; 30 31 void cal(int a, int b, int c) 32 { 33 int &ans = dp[a][b][c]; 34 ans = n; 35 if (a) 36 ans = min(ans, Next[dp[a - 1][b][c] + 1][ss[0][a - 1] - 'a']); 37 if (b) 38 ans = min(ans, Next[dp[a][b - 1][c] + 1][ss[1][b - 1] - 'a']); 39 if (c) 40 ans = min(ans, Next[dp[a][b][c - 1] + 1][ss[2][c - 1] - 'a']); 41 } 42 43 void premanagement() 44 { 45 for (int i = 0; i < 26; i++) 46 { 47 Next[n][i] = Next[n + 1][i] = n; 48 } 49 for (int i = n - 1; i >= 0; i--) 50 { 51 int tmp = str[i] - 'a'; 52 for (int j = 0; j < 26; j++) 53 { 54 if (j != tmp) 55 Next[i][j] = Next[i + 1][j]; 56 else 57 Next[i][j] = i; 58 } 59 } 60 } 61 62 int main() 63 { 64 ios::sync_with_stdio(false); 65 cin.tie(0); 66 //freopen("input.txt", "r", stdin); 67 //freopen("output.txt", "w", stdout); 68 69 cin >> n >> q; 70 cin >> str; 71 premanagement(); 72 dp[0][0][0] = -1; 73 for (int i = 0; i < q; i++) 74 { 75 cin >> opt >> type; 76 type--; 77 if (opt[0] == '+') 78 { 79 cin >> word; 80 ss[type] += word[0]; 81 int max0 = ss[0].size(), max1 = ss[1].size(), max2 = ss[2].size(); 82 int min0 = (type == 0 ? max0 : 0); 83 int min1 = (type == 1 ? max1 : 0); 84 int min2 = (type == 2 ? max2 : 0); 85 for (int a = min0; a <= max0; a++) 86 { 87 for (int b = min1; b <= max1; b++) 88 { 89 for (int c = min2; c <= max2; c++) 90 { 91 cal(a, b, c); 92 } 93 } 94 } 95 } 96 else 97 { 98 ss[type].pop_back(); 99 } 100 101 if (dp[ss[0].size()][ss[1].size()][ss[2].size()] < n) 102 { 103 cout << "YES" << endl; 104 } 105 else 106 { 107 cout << "NO" << endl; 108 } 109 } 110 return 0; 111 }