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;
}
时间: 2024-08-26 23:12:49

UVA 1631-Locker(DP)的相关文章

UVA 1371 - Period(DP)

6.4 一些说明 数据属性可以重写同名的方法属性.这是为了避免在大型系统中产生问题的意外名称冲突.所以用一些减少冲突的常用方法是很有效果的.常用的方法包括:大写字母方法名称,用唯一的字符串来做为数据属性的名称(可以是个下划线_)或者用动词命名方法和用名字命名数据属性. 数据属性就像和对象的普通用户一样可以被方法引用.换句话说,类不能用来实现纯净的数据类型.事实上,在python中不能强制数据隐藏,一切基于约定.(另一方面,如C中写的,python的实现可以做到完全隐藏实现细节并且在必要是可以控制

uva 10271 Chopsticks (DP)

uva 10271 ChopsticksA 题目大意:给出客人数K和筷子数量N(支),总人数M=客人数K + 8(还有家人).要找出M组筷子,每组包括3支筷子,每组都有一个badness值,badness值为每组中较小的两支筷子的差值.求怎样分组,才能使总差值最小. 解题思路:dp[i][j]表示使用第i支筷子,并已组成j组是的最小badness总值.在取筷子时,要注意在所有未被使用的筷子中,要有比选取的两根筷子更长的筷子. #include <cstdio> #include <cst

HDU 4433 locker(DP)( UVA 1631 - Locker)

题目链接:点击打开链接 题意:有一个n位密码锁,每一位都是0~9,可以循环旋转,每次可以让1~3个相邻数字同时往上或者往下旋转一格. 输入初始状态和终止状态,问最少需要转几次. 思路: 很显然是个DP题目, 由于每次可以让相邻的1~3个数字同时旋转, 所以状态的表示上就要考虑相邻3个位置. 那么可以用d[i][a][b][c]表示当前到了第i位,第i位上当前是a,i+1位是b,i+2位是c. 那么显然必须让i位上变成目标数字了, 因为接下来就转移到i+1位了,说明前i位已经全部转到目标态了.  

【UVa】Jump(dp)

http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4198 约瑟夫变形 根据f(n)=(f(n-1)+m)%n 因为是求倒数第几个 那么我们只要求出f(倒数第几个)的值然后再带进去即可. (没理解的自行面壁..) 而f(倒数第几个)=(m-1)%倒数第几个 然后就行了.. #include <cstdio> #include <

Uva 10465-Homer Simpson(DP)

题目链接:点击打开链接 DAG上的最长路. 题意:相当于给两种硬币的面值,每种可以用无限次,问恰好组成S 最多能用多少个硬币.如果不能恰好组成S,输出最大能组成的面值x(x<S)所需的最大硬币数 和 S-x. #include <algorithm> #include <iostream> #include <cstring> #include <cstdlib> #include <string> #include <cctype&

UVA 10163-Storage Keepers(DP)

题目大意:有N(1<=N<=100)个仓库需要看管,有M(1<=M<=30)名应聘者,每个人有能力属性Pi(1<=Pi<=1000).所有仓库都是一样的,每个仓库只能被一人看守,一人可看守多个仓库,当一人看守u个仓库时,每个仓库的安全度为Uj=Pi/u,总安全度为min Uj.雇佣一个能力值为Pi的人需要花费Pi元.求最大的总安全度,和在这样的情况下的最小花费. 先dp一次,求出可能的最大总安全度max,再次dp,求出在安全度为max下的最小花费. 第一次dp:用d[i

UVA 1366-Martian Minging(DP)

题目大意:有一个n(1<=n<=500)行m(1<=m<=500)列的网格,每个网格有两种矿,yeyenum和bloggium,在网格的西边是yeyenum的精炼厂,北边是bloggium的精炼厂,每个网格有一定数量的两种矿,现在要安排一个传送带系统,传送带只能由南向北或者由东向西,传送带同方向的可以连续传,只有传到相应精炼厂才是有效的,问许多能拿到多少矿. 用d[i][j]表示到第i行第j列时,前i行j列能产生的最大的矿数量,用a[i][j]表示在第i行从第1列加到第j列的yey

UVA 473-Raucous Rockers(DP)

题目大意:有n首歌,m个光盘,每个光盘最多能放t时间的歌,给出每首歌的长度,必须按顺序录入光盘(可以选择不录某几首歌),最多能录多少首歌. 用d[i][j][0]表示前i首歌,放j首,最少用多少个光盘,用d[i][j][1]表示前i首歌,放j首,在光盘最少的前提下,最后一个光盘可以剩余的最多容量.根据是否放第i首歌完成递推. 这样递推是正确的,因为前i首歌,放j首的情况下,最佳的情况就是尽量少的用光盘,并且使得最后一个光盘容量尽量大(例如如果用的光盘数多一些,并且最后一个光盘的容量更大一些,这样

【UVa】Salesmen(dp)

http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4170 d[i,j]前i个字符j结尾 d[i,j]=min{d[i-1,k], j和k有边}+(a[i]!=j) 初始化d[1,j]=1, d[1,a[1]]=0 #include <cstdio> #include <cstring> #include <cma

Uva 562-Dividing coins(DP)

题目链接:点击打开链接 题意:n个硬币,给出每个硬币的价值,要求把这些硬币分成两组,使得两组的价值差尽量小. 可以发现如果可以平分,那么价值差肯定为0,那么依次从sum/2 --> 0 枚举i,如果用上述硬币的价值组合可以组成i 那么sum-i-i就是答案.如何判断是否对于一个数i用这些硬币可以凑出来,用背包判就可以了,注意是01背包. #include <algorithm> #include <iostream> #include <cstring> #inc