uva 10739【基础(区间)dp】
题意:给定字符串,可以增加、删除、修改任意字符,问最少经过多少次操作使字符串回文。
题解:定义dp[l][r]表示把从l到r的子串Sl...Sr变成回文串需要操作的最少次数。字符可以增删改,有的博客说增删是一样的,有的说增比删开销大,我倾向于后者,但前者是对的。因为显然s[l]==s[r]时,dp[l][r]=dp[l+1][r-1];当两者不相等时,可以删去s[l]或者s[r],状态转移到dp[l+1][r]+1或dp[l][r-1]+1,但是增加怎么加?一样的,在状态dp[l][r-1]的左边添加's[l-1]'=s[r],或在状态dp[l+1][r]的右边添加's[r+1]'=s[l],画图看看,这和删除确实一样;自然,也可以把s[l],s[r]修改成相同的,状态转移到dp[l+1][r-1]+1。
直接循环里dp:
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<algorithm> 5 using namespace std; 6 7 int dp[1050][1050]; 8 char s[1050]; 9 10 int Min(int a,int b,int c) 11 { 12 int t=min(a,b); 13 return min(t,c); 14 } 15 16 int main() 17 { 18 int T; 19 cin>>T; 20 for(int cas=1;cas<=T;cas++) 21 { 22 cin>>s; 23 int len=strlen(s); 24 for(int i=0;i<len;i++) dp[i][i]=0; 25 for(int i=len-1;i>=0;i--){ 26 for(int j=i+1;j<len;j++){ 27 if(s[i]==s[j]) dp[i][j]=dp[i+1][j-1]; 28 else dp[i][j]=Min(dp[i+1][j],dp[i][j-1],dp[i+1][j-1])+1; 29 } 30 } 31 cout<<"Case "<<cas<<": "<<dp[0][len-1]<<endl; 32 } 33 return 0; 34 }
或者记忆化搜索(dfs):
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<algorithm> 5 using namespace std; 6 7 int dp[1050][1050]; 8 char s[1050]; 9 10 int dfs(int l,int r) 11 { 12 if(dp[l][r]!=-1) return dp[l][r]; 13 if(l>=r) return dp[l][r]=0; 14 if(s[l]==s[r]) 15 dp[l][r]=dfs(l+1,r-1); 16 else 17 dp[l][r]=min(dfs(l+1,r-1),min(dfs(l+1,r),dfs(l,r-1)))+1; 18 return dp[l][r]; 19 } 20 21 int main() 22 { 23 int T; 24 cin>>T; 25 for(int cas=1;cas<=T;cas++) 26 { 27 cin>>s; 28 int len=strlen(s); 29 memset(dp,-1,sizeof(dp)); 30 cout<<"Case "<<cas<<": "<<dfs(0,len-1)<<endl; 31 } 32 return 0; 33 }