问题
hdu 2476 String painter- http://acm.hdu.edu.cn/showproblem.php?pid=2476
paint规则
- 每一次paint操作,至少存在一个字符,从不匹配变得匹配;若操作区间 [ i , j ] [i, j] [i,j] 的首字符不属于这种情况,则首字符可以不操作,依次类推,区间 [ i , j ] [i, j] [i,j] 的首字符一定是从不匹配变得匹配;同理,区间的尾字符亦如此;因此,在目标串b中有 b [ i ] = b [ j ] b[i]=b[ j] b[i]=b[j] 。
- 任意两次paint操作的区间端点不重叠。如果重叠,第一次paint对端点的操作明显是多余的,即可以缩小第一次操作的区间大小。因此,区间 [ i , j ] [i, j] [i,j] 操作可以转化为 [ i , k ] [i, k] [i,k] 和 [ k + 1 , j ] [k+1, j] [k+1,j] 两个子问题。
-
k
k
k 的取值规则:
- k = i k=i k=i,即仅考虑是否paint区间 [ i , j ] [i, j] [i,j] 的首字符,原问题转化为 [ i + 1 , j ] [i+1, j] [i+1,j]
- k = n x t [ k ] k=nxt[k] k=nxt[k],即 paint 了区间 [ i , k ] [i, k] [i,k] 的所有字符,使得 a [ i , ⋯ , k ] = b [ i ] a[i,\cdots , k]=b[i] a[i,⋯,k]=b[i],操作完成后, a [ i ] 、 a [ k ] a[i]、a[k] a[i]、a[k] 实现匹配且 a [ i + 1 , ⋯ , k − 1 ] = b [ i ] a[i+1, \cdots , k-1]=b[i] a[i+1,⋯,k−1]=b[i],此时转化为 [ i + 1 , k − 1 ] [i+1, k-1] [i+1,k−1] 和 [ k + 1 , j ] [k+1, j] [k+1,j] 两个子问题
- n x t [ k ] nxt[k] nxt[k]:在目标串b中, b [ k ] b[k] b[k] 右侧第一个与 b [ k ] b[k] b[k] 相同的字符的位置
方法
一次DP
状态设计
- d p ( i , j , k ) dp(i, j, k) dp(i,j,k):源串A在区间 [ i , j ] [i,j] [i,j] 内的所有字符是k时的最优解
-
k
∈
{
0
、
1
、
2
、
⋯
、
25
、
26
}
k \in \{0、1、2、\cdots、25、26\}
k∈{0、1、2、⋯、25、26}
- 0 、 1 、 2 、 ⋯ 、 25 0、1、2、\cdots、25 0、1、2、⋯、25 表示源串A在区间内的字符都是 a a a 或 b b b 或 ⋯ \cdots ⋯ 或 z z z
- 26 26 26 表示源串A在区间内的字符就是读入的原始字符
- 状态转移
- 如果,仅paint首字符
- 首字符相等 d p ( i , j , k ) = m i n ( d p ( i , j , k ) , d f s ( l + 1 , r , k ) ) dp(i, j, k) = min(dp(i, j, k), dfs(l+1, r, k)) dp(i,j,k)=min(dp(i,j,k),dfs(l+1,r,k))
- 首字符不等 d p ( i , j , k ) = m i n ( d p ( i , j , k ) , 1 + d f s ( l + 1 , r , k ) ) dp(i, j, k) = min(dp(i, j, k), 1+dfs(l+1, r, k)) dp(i,j,k)=min(dp(i,j,k),1+dfs(l+1,r,k))
- 否则, d p ( i , j , k ) = m i n ( d p ( i , j , k ) , 1 + d f s ( l + 1 , t − 1 , b [ s ] − ′ a ′ ) + d f s ( t + 1 , r , k ) ) dp(i, j, k) = min(dp(i, j, k), 1+dfs(l+1, t-1, b[s]-'a')+dfs(t+1, r, k)) dp(i,j,k)=min(dp(i,j,k),1+dfs(l+1,t−1,b[s]−′a′)+dfs(t+1,r,k))
- 如果,仅paint首字符
代码【46MS】
/* hdu 2476 String Painter 区间dp*/
#include<bits/stdc++.h>
using namespace std;
const int MXN = 110;
const int inf = 0x7f7f7f7f;
int n, nxt[MXN], dp[MXN][MXN][27];
char a[MXN], b[MXN];
int dfs(int l, int r, int ch){
int &D = dp[l][r][ch];
if(D < inf) return D;
if(l > r) return D = 0;
int s = l, t;
for(t = l; t <= r; t = nxt[t]){
if(s == t){
if((ch==26?a[s]:(ch+'a')) == b[s]) D = min(D, dfs(l+1, r, ch));
else D = min(D, 1+dfs(l+1, r, ch));
}
else D = min(D, 1+dfs(l+1, t-1, b[s]-'a')+dfs(t+1, r, ch));
}
return D;
}
int main(){
int c[26];
while(scanf("%s%s", a, b) == 2){
memset(dp, inf, sizeof dp);
n = strlen(a);
for(int i = 0; i < 26; ++i) c[i] = n;
for(int i = n-1; i >= 0; --i) nxt[i] = c[b[i]-'a'], c[b[i]-'a'] = i;
printf("%d\n", dfs(0, n-1, 26));
}
return 0;
}
二次DP_1
2维状态
- d p ( i , j ) dp(i, j) dp(i,j):区间 [ i , j ] [i, j] [i,j] 内每一个字符至少paint一次时的最优解【即不考虑源串a与目标串b的初始匹配情况】
1维状态
- f ( i ) f(i) f(i): a [ i ] a[i] a[i] 是最后一个被paint次数为0的字符时的最优解
代码【31MS】
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e2+5;
char s[maxn],t[maxn];
int dp[maxn][maxn],n,f[maxn];
int main() {
while (~scanf("%s%s",s+1,t+1)){
n=strlen(s+1);
for (int i=1;i<=n;i++)dp[i][i]=1;
for (int len=2;len<=n;len++){
for (int i=1;i<=n;i++){
int j=i+len-1;
if (j>n)break;
dp[i][j]=dp[i+1][j]+1;
for (int k=i+1;k<=j;k++){
if (t[i]==t[k])dp[i][j]=min(dp[i][j],dp[i+1][k]+dp[k+1][j]);
}
}
}
for (int i=1;i<=n;i++){
f[i]=dp[1][i];
if (s[i]==t[i])f[i]=f[i-1];
for (int j=1;j<i;j++)f[i]=min(f[i],f[j]+dp[j+1][i]);
}
printf("%d\n",f[n]);
}
return 0;
}
代码【0-15MS】
// hdu 2476 String painter
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define MXN 110
char A[MXN], B[MXN];
int dp[MXN][MXN], n, f[MXN], nxt[MXN];
int main() {
int s, t, tmp;
while (scanf("%s%s", A+1, B+1) == 2) {
n = 0;
while(A[n+1]) n++;
for(int i = 1; i <= 256; ++i) f[i] = n+1;
for(int i = n; i >= 1; --i) nxt[i] = f[B[i]], f[B[i]] = i;
for(int i = 1; i <= n; ++i) dp[i][i] = 1;
for(int len = 2; len <= n; ++len){
for(int j = 1; j <= n; ++j){
s = j, t = j+len-1;
if(t > n) break;
dp[j][t] = dp[j+1][t]+1;
s = nxt[s];
while(s <= t){
tmp = dp[j+1][s];
if(s+1 <= t) tmp += dp[s+1][t];
dp[j][t] = min(dp[j][t], tmp);
s = nxt[s];
}
}
}
f[0] = 0;
for(int i = 1; i <= n; ++i){
f[i] = f[i-1] + (A[i] == B[i] ? 0 : 1);
for(int j = 0; j < i; ++j){
f[i] = min(f[i], f[j]+dp[j+1][i]);
}
}
printf("%d\n", f[n]);
}
return 0;
}
代码
// hdu 2476 String painter
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define MXN 150
char A[MXN], B[MXN];
int dp[MXN][MXN], n, f[MXN], nxt[MXN];
int main() {
int s, t, tmp;
while (scanf("%s%s", A+1, B+1) == 2) {
n = 0;
while(A[n+1]) n++;
for(int i = 1; i <= 256; ++i) f[i] = n+1;
for(int i = n; i > 0; --i) {
nxt[i] = f[B[i]];
f[B[i]] = i;
}
for(int i = 1; i <= n; ++i) dp[i][i] = 1;
for(int len = 2; len <= n; ++len){
for(int j = 1; j <= n; ++j){
s = j, t = j+len-1;
if(t > n) break;
dp[j][t] = dp[j+1][t]+1;
s = nxt[s];
while(s <= t){
tmp = dp[j+1][s];
if(s+1 <= t) tmp += dp[s+1][t];
dp[j][t] = min(dp[j][t], tmp);
s = nxt[s];
}
}
}
for(int i = n; i >= 0; --i) nxt[i] = A[i+1] == B[i+1] ? (i+1) : nxt[i+1];
f[0] = 0;
for(int i = 1; i <= n; ++i){
if(A[i] == B[i]){
f[i] = f[i-1];
continue;
}
else f[i] = dp[1][i];
for(int j = nxt[0]; j < i; j = nxt[j]){
f[i] = min(f[i], f[j-1] + dp[j+1][i]);
}
}
printf("%d\n", f[n]);
}
return 0;
}
二次DP_2
3维状态
- dp(i, j, k)的定义:源串A在区间(i,j)内的所有字符都是k时的最优解
2维状态
- f(i, j)的定义:区间(i,j)的最优解
代码【202MS】
#include<bits/stdc++.h>
using namespace std;
#define MXN 110
#define INF 0x3f3f3f3f
int N, nxt[MXN], C[256], dp[MXN][MXN][26], f[MXN][MXN];
string A, B;
int main(){
int s, t, tmp;
while(cin >> A >> B){
N = A.size();
for(int i = 0; i < 256; i++) C[i] = N;
for(int i = N-1; i >= 0; i--){
nxt[i] = C[B[i]];
C[B[i]] = i;
}
memset(dp, INF, sizeof dp);
memset(f, INF, sizeof f);
for(int i = 1; i <= N; i++){ // 长度
for(int j = 0; j <= N - i; j++){ // 起点
for(char k = 'a'; k <= 'z'; k++){
s = j, t = j+i-1;
while(s <= t){
tmp = B[j] == k ? 0 : 1;
if(j+1 <= s-1) tmp += dp[j+1][s-1][B[j]-'a'];
if(s+1 <= t) tmp += dp[s+1][t][k-'a'];
dp[j][t][k-'a'] = min(dp[j][t][k-'a'], tmp);
s = nxt[s];
}
}
}
}
for(int i = 1; i <= N; i++){
for(int j = 0; j <= N-i; j++){
s = j, t = j+i-1;
while(s <= t){
if(B[j] == A[j] && B[s] == A[s])
tmp = (j+1 <= s-1) ? f[j+1][s-1] : 0;
else{
tmp = (j+1 <= s-1) ? dp[j+1][s-1][B[j]-'a'] : 0;
tmp += 1;
}
if(s+1 <= t) tmp += f[s+1][t];
f[j][t] = min(f[j][t], tmp);
s = nxt[s];
}
}
}
printf("%d\n", f[0][N-1]);
}
return 0;
}