题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4433
这是一道2012年ACM天津赛区现场赛的题目,大意是给出两串数字,求用最少的转换次数将一串(A)变为另一串(B)。
转换规则是:可以将连续的1到3位数字都加一或者减一(0-9的数字是循环的,0减一变9,9加一变0)
本题的数字串的长度最大有1000,光用搜索是不行的了,正解是DP
DP[i][j][k]表示前i-2个数字与目的串的相同,且第i-1位为j , 第i 位为k 的最小变换次数。
那么状态的转移就是 DP[i][j][k]=min{DP[i-1][ X ][ Y ]+dis(A[i],K)} ,其中dis(A[i],K)表示从A[i]变为K所需的变化次数,不难知道变化可以往上变化,也可以往下变化,所以有两种情况;X表示在变化次数不超过dis(A[i],K)和Y的变化次数的情况下能变成B[i-2] 的数字,Y表示在变化次数不超过dis(A[i],K)的情况下能变成 J 的数字,X和Y都需要根据当前dis(A[i],K)的计算状态(向上向下)来定。
X、Y的变化次数有所限制,是以第i位的变化次数为标准,这样可以保证枚举到所有连续变化的情况,比如说234的次数和444的次数是一致的。对于每一位数都能找到一种满足这种条件的方案使得穷尽与这位和相临的2位的各种组合情况。
这题能用动态规划的关键是每次变化的数字位一定是连续的,这为无后效性和最优子结构提供支持。状态的转移比较麻烦。
代码:
1 #include <stdio.h> 2 #include <string.h> 3 char a[1280],b[1280]; 4 int dp[1280][10][10]; 5 bool ifv[1280][10][10]; 6 inline int min(int a,int b){return a<b?a:b;} 7 void deal(char *p) 8 { 9 int n=strlen(p); 10 for (int i=0;i<n;++i) p[i]-='0'; 11 } 12 int dps(int ii,int jj,int kk) 13 { 14 if (ii<1) return min((kk-a[ii]+10)%10,(a[ii]-kk+10)%10); 15 if (ifv[ii][jj][kk]) return dp[ii][jj][kk]; 16 ifv[ii][jj][kk]=true; 17 dp[ii][jj][kk]=200000000; 18 int tem=(kk-a[ii]+10)%10; 19 for (int i=0;i<=tem;++i) 20 for (int j=0;j<=i;++j) 21 { 22 dp[ii][jj][kk]=min(dp[ii][jj][kk],dps(ii-1,(b[ii-2]-j+10)%10,(jj-i+10)%10)+tem); 23 } 24 tem=10-tem; 25 for (int i=0;i<=tem;++i) 26 for (int j=0;j<=i;++j) 27 { 28 dp[ii][jj][kk]=min(dp[ii][jj][kk],dps(ii-1,(b[ii-2]+j)%10,(jj+i)%10)+tem); 29 } 30 return dp[ii][jj][kk]; 31 } 32 int main() 33 { 34 //freopen("in.txt","r",stdin); 35 while (~scanf("%s%s",a,b)) 36 { 37 memset(dp,0,sizeof dp); 38 memset(ifv,0,sizeof ifv); 39 int len=strlen(b); 40 deal(a);deal(b); 41 printf("%d ",dps(len-1,b[len-2],b[len-1])); 42 } 43 }