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<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;
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值