题目链接:点击打开链接
题意:有一个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位已经全部转到目标态了。 那么转移也就很显然了, 计算出a上转和下转的次数, 然后b、c位的数字有选择的转多少次。
细节参见代码:
#include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<string> #include<vector> #include<stack> #include<bitset> #include<cstdlib> #include<cmath> #include<set> #include<list> #include<deque> #include<map> #include<queue> #define Max(a,b) ((a)>(b)?(a):(b)) #define Min(a,b) ((a)<(b)?(a):(b)) using namespace std; typedef long long ll; const double PI = acos(-1.0); const double eps = 1e-6; const int mod = 1000000000 + 7; const int INF = 1000000000; const int maxn = 1000 + 10; int T,n,m,kase=0,d[maxn][15][15][15],vis[maxn][15][15][15]; char s1[maxn], s2[maxn]; int dp(int i, int a, int b, int c) { int& ans = d[i][a][b][c]; if(i >= n) return 0; if(vis[i][a][b][c] == kase) return ans; vis[i][a][b][c] = kase; int t = s2[i] - '0'; ans = INF; if(a < t) { int xia = t - a; for(int j=0;j<=xia;j++) { for(int k=0;k<=j;k++) { int v = (b + j)%10, u = (c + k)%10; ans = min(ans, dp(i+1, v, u, s1[i+3]-'0')+xia); } } int shang = 10 - xia; for(int j=0;j<=shang;j++) { for(int k=0;k<=j;k++) { int v = (b - j + 10)%10, u = (c - k + 10)%10; ans = min(ans, dp(i+1, v, u, s1[i+3]-'0')+shang); } } } else { int shang = a - t; for(int j=0;j<=shang;j++) { for(int k=0;k<=j;k++) { int v = (b - j + 10)%10, u = (c - k + 10)%10; ans = min(ans, dp(i+1, v, u, s1[i+3]-'0')+shang); } } int xia = 10 - shang; for(int j=0;j<=xia;j++) { for(int k=0;k<=j;k++) { int v = (b + j)%10, u = (c + k)%10; ans = min(ans, dp(i+1, v, u, s1[i+3]-'0')+xia); } } } return ans; } int main() { while(~scanf("%s%s",s1,s2)) { ++kase; n = strlen(s1); for(int i=n;i<=n+1;i++) { s1[i] = s2[i] = '0'; } s1[n+4] = s2[n+4] = '\0'; printf("%d\n",dp(0,s1[0]-'0',s1[1]-'0',s1[2]-'0')); } return 0; }
时间: 2024-10-13 21:01:22