网易雷火笔试-打印机(区间dp)
有一台神奇的打印机,可打印的字母范围大写A-Z,每次只能从纸带上的任意位置开始打印同一字母任意次数,并且可以覆盖之前同一位置上已经打印上的字母。给定一个目标字符串,问最少需要打印多少次才能打印出给定字符串。例如,目标为ABCBA,先打印AAAAA,再打印BBB,再打印C,所以答案为3。
分析:没什么难的,看到有划水过去的,但是答案是错的,有求联通块的,不理解是怎么建的图
我是用的区间dp,dp[i][j]表示打印【i,j】需要的打印数,[l,r]最差的选择是在[l,j-1]的基础上在末尾单独的添加上那一个字符,所以dp[i][j]最大为dp[i][j-1]+1
那么dp[i][j]=min(dp[i][t]+d[t+1][j-1]),但是要注意会产生非法访问(即i>j)dp的只有在类似"AA"这样的子串的时候才会发生,将dp非法访问的都置为1就好了,或者这样情况手动处理
有的人思路认为如果找到一个t<j使得s[t]==s[j],那么dp[i][j]=dp[i][t]+{(t+1)-(j-1)之间的不同字符数},但是ADBDBA这样的样例是过不去的
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=1e3+5; 4 int dp[maxn][maxn]; 5 char s[maxn]; 6 7 int solve(){ 8 int n=strlen(s); 9 memset(dp,0,sizeof(dp)); 10 for(int i=0;i<n;i++)dp[i][i]=1; 11 12 for(int k=2;k<=n;k++){ 13 for(int i=0;i<=n-k;i++){ 14 int j=i+k-1; 15 dp[i][j]=dp[i][j-1]+1; 16 for(int t=j-1;t>=i;t--) 17 if(s[t]==s[j]) 18 dp[i][j]=min(dp[i][t]+dp[t+1][j-1],dp[i][j]); 19 //cout<<i<<" "<<j<<" "<<dp[i][j]<<endl; 20 } 21 22 } 23 return dp[0][n-1]; 24 } 25 26 int main(){ 27 cin>>s; 28 cout<<solve()<<endl; 29 30 return 0; 31 }