题目大意:给出两串长度为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;
}