4576 | njczy2010 | C | Accepted | 860 KB | 140 ms | G++ | 2063 B | 2014-10-16 09:51:19 |
哎,为啥1000*100*100的复杂度的dp就不敢敲了呢,,,真是2
涉及到可能有后效性的时候,一维就不行了,要扩维。本题,一个状态的变化会影响后两个,所以要用三维。
lockerTime Limit: 6000/3000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 1418 Accepted Submission(s): 620
Problem Description
A password locker with N digits, each digit can be rotated to 0-9 circularly. You can rotate 1-3 consecutive digits up or down in one step. For examples: 567890 -> 567901 (by rotating the last 3 digits up) 000000 -> 000900 (by rotating the 4th digit down) Given the current state and the secret password, what is the minimum amount of steps you have to rotate the locker in order to get from current state to the secret password?
Input
Multiple (less than 50) cases, process to EOF. For each case, two strings with equal length (≤ 1000) consists of only digits are given, representing the current state and the secret password, respectively.
Output
For each case, output one integer, the minimum amount of steps from the current state to the secret password.
Sample Input
111111 222222 896521 183995
Sample Output
2 12
Source
Recommend
zhoujiaqi2010 | We have carefully selected several similar problems for you: 5065 5064 5063 5062 5061
题目:给出两个串,每次可以选择连续的1-3个数字,进行同时加1或者同时减1,问最少经过多少次操作,将一个串转变为另外一个串
http://acm.hdu.edu.cn/showproblem.php?pid=4433
以前有类似的题目,BFS就可以了
不过这题的数据量太大,听说也有不少是搜索过的
我写了一个矬B的搜索,反正是挂了,没加什么优化
训练的时候,yobobobo的DP解法比较接近,可是最终貌似卡在后效性上?
dp[i][j][k]表示 前i个已经完全匹配,而这时候,第i+1个已经加了j位,第i+2位已经加了k
转移分为两步,枚举加,枚举减
注意如果第i位加了a,第i+1位加了b,第i+2位加了c,那么a>=b>=c这个关系不能错
以下题解转自:http://www.cnblogs.com/kuangbin/archive/2012/10/27/2742672.html
这题的意思就相当于是一个数字密码锁。
每次可以正向或者反向旋转连续的1-3个数字。求从现在状态转到目标状态需要的最少步数。
题目给了两个长度一样的由0-9组成的字符串。就相当于每次操作可以选择连续的1-3个数字加1或者减1.这不过这个加和减是循环的。0减1到9,9加1到0.
一看就是DP。
这不过DP方程不好想,也不好表示状态。
dp[i][x][y]表示处理到第i个,后面两个数字是x,y,把前i位转正确需要的最少步数。
计算dp[i][x][y]时,前i-2位是题目给的现在状态的值,第i-1位是x,第i位是y,就是把前i位转正确。
要把dp[i]的状态转移到dp[i-1]去。
把第i位从x转到目标态b[i]去,就可以把状态转移了。
和第i位相关的转动有三种:一是单转第i位,二是转第i位和第i-1位,三是转第i位、第i-1位和第i-2位。
根据三种可以确定 dp[i-1][xx][yy]中的xx,yy;
转动分为正转和反转。
如果第i位是正转,转正确需要d1步。
那么第i-1和第i-2位正转的不是是小于等于d1的。而且i-2的步数小于等于i-1。
如果第i位是正转,转正确需要d2步。
那么第i-1和第i-2位正转的不是是小于等于d2的。而且i-2的步数小于等于i-1。
这样DP的时候i从1到n转移过去。
处理dp[i]的时候,dp[1~(n-1)][0~9][0~9]都是已知的。就很容易确定dp[i][0~9][0~9]的最小值了。
注意处理的是初始化过程,全部初始化为INF,dp[0][0][0]=0;
还有转移的时候,i==1和i==2单独处理下。。
转移最多是1000*100*100。
是可以接受的。
我一开始做的时候增加了以为dp[i][x][y][z]表示了后面三位,好理解,但是TLE了。
减少一维表示就AC了。
具体看代码吧!
注释的很清楚了。
1 #include<iostream> 2 #include<cstring> 3 #include<cstdlib> 4 #include<cstdio> 5 #include<algorithm> 6 #include<cmath> 7 #include<queue> 8 #include<map> 9 #include<set> 10 #include<string> 11 //#include<pair> 12 13 #define N 1005 14 #define M 1000005 15 #define mod 1000000007 16 #define inf 0x3f3f3f3f 17 //#define p 10000007 18 #define mod2 100000000 19 #define ll long long 20 #define LL long long 21 #define maxi(a,b) (a)>(b)? (a) : (b) 22 #define mini(a,b) (a)<(b)? (a) : (b) 23 24 using namespace std; 25 26 char s1[N]; 27 char s2[N]; 28 int a[N]; 29 int dp[N][12][12]; 30 int n; 31 32 void ini() 33 { 34 n=strlen(s1); 35 memset(dp,inf,sizeof(dp)); 36 int i; 37 a[0]=0; 38 for(i=0;i<n;i++){ 39 a[i+1]=s1[i]-s2[i]; 40 if(a[i+1]<0) a[i+1]+=10; 41 } 42 // for(i=1;i<=n;i++){ 43 // printf("%d",a[i]); 44 // }printf(" "); 45 a[n+1]=0; 46 a[n+2]=0; 47 } 48 49 50 void solve() 51 { 52 int i,j,k,jj,kk,t1,t2; 53 dp[0][0][0]=0; 54 for(i=1;i<=n;i++){ 55 for(j=0;j<=9;j++){ 56 for(k=0;k<=9;k++){ 57 t1=(a[i]-j+10)%10; 58 for(jj=0;jj<=t1;jj++){ 59 for(kk=0;kk<=jj;kk++){ 60 dp[i][ (jj+k)%10 ][kk]=min(dp[i][ (jj+k)%10 ][kk],dp[i-1][j][k]+t1); 61 } 62 } 63 64 t2=10-t1; 65 for(jj=0;jj<=t2;jj++){ 66 for(kk=0;kk<=jj;kk++){ 67 dp[i][ (k-jj+10)%10 ][(10-kk)%10]=min(dp[i][ (k-jj+10)%10 ][(10-kk)%10],dp[i-1][j][k]+t2); 68 } 69 } 70 } 71 } 72 } 73 74 // for(i=1;i<=n;i++){ 75 // for(j=0;j<=9;j++){ 76 // for(k=0;k<=9;k++) printf(" i=%d j=%d k=%d dp=%d ",i,j,k,dp[i][j][k]); 77 // } 78 // } 79 return ; 80 } 81 82 void out() 83 { 84 printf("%d ",dp[n][0][0]); 85 } 86 87 int main() 88 { 89 //freopen("data.in","r",stdin); 90 //freopen("data.out","w",stdout); 91 //scanf("%d",&T); 92 // for(int ccnt=1;ccnt<=T;ccnt++) 93 // while(T--) 94 while(scanf("%s%s",s1,s2)!=EOF) 95 { 96 //if(n==0 && k==0 ) break; 97 //printf("Case %d: ",ccnt); 98 ini(); 99 solve(); 100 out(); 101 } 102 103 return 0; 104 }