HDU4433 Locker(DP)

locker

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1763    Accepted Submission(s): 800

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

這題的意思就相當於是一個數字密碼鎖。
每次可以正向或者反向旋轉連續的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了。
 
 
具體看代碼吧!註釋的很清楚了。

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>

using namespace std;
const int MAXN=1010;
const int INF=100000000;
char str1[MAXN],str2[MAXN];
int a[MAXN],b[MAXN];
int dp[MAXN][10][10];
//dp[i][x][y]表示處理到第i個,後面兩位是xy時的最小操作數

int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int n;
    int x,y;
    int xx,yy;
    int i,j,k;
    while(scanf("%s%s",&str1,&str2)!=EOF)
    {
        n=strlen(str1);
        for(i=1;i<=n;i++)
        {
            a[i]=str1[i-1]-'0';
            b[i]=str2[i-1]-'0';
        }
        for(i=0;i<=n;i++)
          for(x=0;x<10;x++)
            for(y=0;y<10;y++)
                dp[i][x][y]=INF;
        //初始化
        dp[0][0][0]=0;
        for(i = 1 ; i <= n ; i++)//1到n進行DP
              for(x = 0 ; x < 10 ; x++)//這是第i-1個數
              {
                  if(i <= 1 && x > 0) break;
                  for(y = 0 ; y < 10 ; y++)//這是第i個數
                  {
                      //增加和減少變換
                      //把第i個數調整正確,需要的步數
                      int d1 = (b[i] - y + 10)%10;//這是正向轉
                      int d2 = (y - b[i] + 10)%10;//這是逆向轉

                //下面的DP過程就是把i的狀態表示成i-1的狀態
                      if(i==1)
                      {
                          xx=0;
                          yy=0;
                          dp[i][x][y]=min(dp[i][x][y],dp[i-1][xx][yy]+min(d1,d2));
                          continue;
                      }
                      if(i==2)
                      {
                          xx=0;//i-1的倒數第2位一定是0

                          //下面是枚舉i-1的倒數第一位
                          for(j=x;j<=x+d1;j++)
                          {
                              yy=j%10;
                              dp[i][x][y]=min(dp[i][x][y],dp[i-1][xx][yy]+d1);
                          }
                          for(j=x;j>=x-d2;j--)
                          {
                              yy=(j+10)%10;
                              dp[i][x][y]=min(dp[i][x][y],dp[i-1][xx][yy]+d2);
                          }
                          continue;
                      }
                      //枚舉i-1的倒數第1位和倒數第2位
                      for(j=0;j<=d1;j++)
                        for(k=j;k<=d1;k++)
                        {
                            xx=(a[i-2]+j)%10;
                            yy=(x+k)%10;
                            dp[i][x][y]=min(dp[i][x][y],dp[i-1][xx][yy]+d1);
                        }
                     for(j=0;j<=d2;j++)
                       for(k=j;k<=d2;k++)
                       {
                           xx=(a[i-2]-j+10)%10;
                           yy=(x-k+10)%10;
                           dp[i][x][y]=min(dp[i][x][y],dp[i-1][xx][yy]+d2);
                       }
                  }
              }
        if(n>=2)x=a[n-1];
        else x=0;
        if(n>=1)y=a[n];
        else y=0;
        printf("%d\n",dp[n][x][y]);
    }
    return 0;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章