题目大意:给出两串长度为n(n<=1000)的只含数字的串,每次操作可以将连续的1到3个数字模10加1或模10减1(即9+1=0,0-1=9),求源串最少多少步能变成目标串。
首先假定,对于任意操作,把最后一个操作数当做操作的操作数。比如000一步变为111把操作数看成第3个数。
预处理出数组cost,cost[i][j]表示在只有最后一个数字是操作数的情况下,从数字i变到数字j要的最少步数(i,j<1000)。例如111变为220为3,只能操作最后一个数,所以111-->222->221-->220。
用a[i]和b[i]表示源数字串和目标数字串的第i个数字。
用d[i][j]表示前i个数字,前i-2个已经变成目标数字,且第i-1个和第i个组成十进制数j的情况下,最小需要的步数。
根据在操作数是i-1和i-1之前的所有操作都完成的情况下的状态d[i-1][u]完成递推。
若前一个状态是d[i-1][u](此时源串的第i个数并没有变),那么此时转移就是只操作最后一个数的情况下,将三位数u*10+a[i]变为三位数b[i-2]*100+j(在状态d[i][j]中,第i-2个数已经变为目标数字),正好是cost数组存储的内容,从而完成递推。
状态转移方程:
d[i][j]=min { d[i-1][u]+cost[q][p] }
其中p=b[i-2]*100+j,q=u*10+a[i]
#include<stdio.h> #include<stdlib.h> #include<string.h> char a[1010]; char b[1010]; int d[2][110]; int cost[1010][1010]; int main(void) { int i,j,u,p,q,i1,i2,i3,j1,j2,j3,u1,u2,u3,lo,cur,minp; for(i=0;i<1000;i++) { for(j=0;j<1000;j++) { if(i!=j) { if(cost[j][i]>0) { cost[i][j]=cost[j][i]; } else { i1=i/100; i2=(i%100)/10; i3=i%10; j1=j/100; j2=(j%100)/10; j3=j%10; if(i1<j1) { u1=j1-i1; i2=(i2+u1)%10; i3=(i3+u1)%10; } else { u1=i1-j1; i2=(i2-u1+10)%10; i3=(i3-u1+10)%10; } if(i2<j2) { u2=j2-i2; i3=(i3+u2)%10; } else { u2=i2-j2; i3=(i3-u2+10)%10; } u3=(i3>j3)?i3-j3:j3-i3; u1=(u1>10-u1)?10-u1:u1; u2=(u2>10-u2)?10-u2:u2; u3=(u3>10-u3)?10-u3:u3; cost[i][j]=u1+u2+u3; } } } } while(scanf("%s%s",a+1,b+1)==2) { lo=strlen(a+1); if(lo==1) { u=(a[1]>b[1])?a[1]-b[1]:b[1]-a[1]; printf("%d\n",(u>10-u)?10-u:u); } else { for(j=0;j<10;j++) { u=(j>a[1]-'0')?j-a[1]+'0':a[1]-'0'-j; d[0][j]=(u>10-u)?10-u:u; } for(j=0;j<100;j++) { minp=(1<<30); for(u=0;u<10;u++) { q=u*10+a[2]-'0'; minp=(d[0][u]+cost[q][j]<minp)?d[0][u]+cost[q][j]:minp; } d[1][j]=minp; } cur=1; for(i=3;i<=lo;i++) { cur^=1; for(j=0;j<100;j++) { minp=(1<<29); for(u=0;u<100;u++) { p=(b[i-2]-'0')*100+j; q=u*10+a[i]-'0'; minp=(d[cur^1][u]+cost[p][q]<minp)?d[cur^1][u]+cost[p][q]:minp; } d[cur][j]=minp; } } p=(b[lo-1]-'0')*10+b[lo]-'0'; printf("%d\n",d[cur][p]); } } return 0; }
原文地址:http://blog.csdn.net/dilemma729/article/details/43976119