UVA 1631-Locker(DP)

题目大意:给出两串长度为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
#include
#include
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(i1j3)?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]


你可能感兴趣的:(ACM_Uva,ACM_DP&递推)