zoukankan      html  css  js  c++  java
  • UVa1631

    题意:

    有一个n(n≤1000)位密码锁,每位都是0~9,可以循环旋转。每次让1~3个相邻数字同时往上或者往下转一格,567890->567901(最后3位向上)。输入初始状态和终止状态(长度不超过1000),问最少要转几次。

    这道题刚开始看有点蒙,不知道应该如何转密码锁上面的格,然后通过经验能够感觉出来最优的转动方案是可以按照

    从左到右的顺序来移动到正确的位置的,这样我们将无序的转动变成了有序的转动,自然问题就简化了许多,

    然后通过观察题意(每次让1~3个相邻数字同时往上或者往下转一格),我发现1-3个相邻的数字说明了如果按照从左到右的

    顺序进行转动后那么可以仅仅影响前面或后面1-3个格子的,然后发现影响后面的格子会更好写程序,所以下面是状态与转移方程:
      d(i,j,k,p)表示前i-1个已经配对,其中i、i+1、i+2分别对应j、k、p还需要最少波动次数
      状态转移方程:其中第i个一定要拨动使之配对,其余的i+1、i+2可以随着i的拨动进行改变(也可以不改变),可以往上下拨。
      最后答案为d(0,a[0],a[1],a[2])

    这需要使用记忆化搜索求解,刚开始我拨动的时候犯了一个错误,如果i拨动x次,那么i+1、i+2也一定拨动x次,或者仅仅i+1拨动x次,

    这样其实是错误的(想一想,为什么),后面拨动的次数是任意的(但需要保证小于前面拨动次数),下面是代码:

    // UVa 1631 
    /*
      d(i,j,k,p)表示前i-1个已经配对,其中i、i+1、i+2分别对应j、k、p还需要最少波动次数
      状态转移方程:其中第i个一定要拨动使之配对,其余的i+1、i+2可以随着i的拨动进行改变(也可以不改变),可以往上下拨。
      最后答案为d(0,a[0],a[1],a[2])
    */
    #include <cstdio> 
    #include <cstring> 
    #include <algorithm>
    using namespace std; 
    
    const int maxn = 1000 + 10; 
    const int INF = 1000000003; 
    
    char s[maxn], s2[maxn];
    int len, a[maxn], b[maxn]; 
    int d[maxn][11][11][11]; 
    
    int dp(int cur, int x, int y, int z) { 
      if (cur >= len) return 0; 
      
      int& ans = d[cur][x][y][z]; 
      if (ans >= 0) return ans; 
      
      ans = INF;
      int down = (b[cur]+10-x)%10, up = (x+10-b[cur])%10;
      for (int i = 0; i <= down; ++i) 
        for (int j = 0; j <= i; ++j) 
          ans = min(ans, dp(cur+1,(y+i)%10,(z+j)%10,a[cur+3])+down);
      for (int i = 0; i <= up; ++i) 
        for (int j = 0; j <= i; ++j) 
          ans = min(ans, dp(cur+1,(y+10-i)%10,(z+10-j)%10,a[cur+3])+up);
      return ans; 
    }
    
    int main() { 
      while (scanf("%s%s", s, s2) == 2) {
          len = strlen(s); 
          for (int i = 0; i < len; ++i) a[i] = s[i] - '0'; 
          for (int i = 0; i < len; ++i) b[i] = s2[i] - '0';
          a[len] = a[len+1] = a[len+2] = b[len] = b[len+1] = b[len+2] = 0;   
          memset(d, -1, sizeof(d)); 
          printf("%d
    ", dp(0,a[0],a[1],a[2]));
      }
      return 0;
    }
  • 相关阅读:
    关于求LCA三种方法
    逆序对与本质不同的逆序对
    缩点+割点(tarjan)
    关于线段树
    引爆点
    0 基础认知产品经理
    一款 App 开发到上架
    坚持+时间管理
    测试流程
    Java学习笔记--字符串String、StringBuffer和StringBuilder
  • 原文地址:https://www.cnblogs.com/yifeiWa/p/11330489.html
Copyright © 2011-2022 走看看