UVa Live 4394 String painter - 动态规划

题目传送门

  传送门

题目大意

  给定两个字符串$s$,$t$,每次可以选择一个$s$的一个区间将它变成一个字符,问最后将$s$变成$t$的最少操作数。

  当$s$某一段中每个字符都不同的时候,等价于对一个空串操作变成$t$的这一段。

  考虑用$g_{l, r}$表示将空串,变成$t_{l}t_{l + 1}\cdots t_{r}$的最少操作数。

  不难发现,如果我对一个区间$[l, r]$进行操作,满足下面两个条件不会更劣:

  • $t_{l} = t_{r}$
  • 在这之后的操作要么被$[l + 1, r - 1]$包含,要么与$[l, r]$无交。

  第一点比较显然,第二点是因为如果之后的操作覆盖了$r$或者覆盖了$l$,那么这一次操作对$r$或者$l$的修改是无效的,我可以把它替换成更小的区间。

  每次考虑对当前区间的第一次操作,转移可以分为下面几个:

  • 如果是对整个区间进行操作,那么它必然满足$t_{l} = t_{r}$,它的操作次数等于$g_{l + 1, r}$。
  • 如果没有对整个区间进行操作,那么考虑对包含左端点或者右端点的一个操作。
    等价于在中间枚举一个中间点$i$,使得$t_{i} = t_{r}$或者$t_{i} = t_{s}$用$g_{l, i - 1} + g_{i, r}$或者$g_{l, i} + g_{i + 1, r}$。

  考虑上原来的串$s$与空串有什么不同,如果一个位置上$s_{i} = t_{i}$,那么这个位置可以不用操作。如果我硬点它不操作,那么所有操作区间都不能跨过它。(即使操作的字符和它相等,但那样和它被看成空没有什么不同)。

  所以再设$f_{i}$表示使$s$的前$i$个字符与$t$相同的最小代价。这个转移比较显然。

  • 如果$s_{i} = t_{i}$,那么用$f_{i - 1}$更新$f_{i}$
  • 否则枚举最后一个被当成空串的区间。

  时间复杂度$O(n^{3})$

Code

 1 /**
 2  * UVa Live
 3  * Problem#4394
 4  * Accepted
 5  * Time: 43ms
 6  */
 7 #include <iostream>
 8 #include <cstdlib>
 9 #include <cstring>
10 #include <cstdio>
11 using namespace std;
12 typedef bool boolean;
13 
14 template <typename T>
15 void pfill(T* pst, const T* ped, T val) {
16     for (; pst != ped; *(pst++) = val);
17 }
18 
19 const int N = 105;
20 const signed inf = (signed) (~0u >> 1);
21 
22 int n;
23 char A[N], B[N];
24 int f[N], g[N][N];
25 boolean vis[N][N];
26 
27 inline boolean init() {
28     if (scanf("%s", A + 1) == -1)
29         return false;
30     scanf("%s", B + 1);
31     n = strlen(A + 1);
32     return true;
33 }
34 
35 int dp(int l, int r) {
36     if (l == r)
37         return 1;
38     if (vis[l][r])
39         return g[l][r];
40     int& rt = g[l][r];
41     rt = inf, vis[l][r] = true;
42     if (B[l] == B[r])
43         rt = min(dp(l + 1, r), dp(l, r - 1));
44 
45     for (int i = l; i < r; i++)
46         if (B[l] == B[i])
47             rt = min(rt, dp(l, i) + dp(i + 1, r));
48     for (int i = l + 1; i <= r; i++)
49         if (B[i] == B[r])
50             rt = min(rt, dp(l, i - 1) + dp(i, r));
51     return rt;
52 }
53 
54 inline void solve() {
55     pfill(vis[1], vis[n + 1], false);
56     f[0] = 0;
57     for (int i = 1; i <= n; i++) {
58         f[i] = inf;
59         if (A[i] == B[i])
60             f[i] = f[i - 1];
61         for (int j = i; j; j--)
62             f[i] = min(f[i], f[j - 1] + dp(j, i));
63     }
64     printf("%d\n", f[n]);
65 }
66 
67 int main() {
68     while (init())
69         solve();
70     return 0;
71 }

猜你喜欢

转载自www.cnblogs.com/yyf0309/p/9912275.html