题意:给定两个密码串,每次可以让1~3个相邻的密码向上或者向下滚动,每个密码是 0−9 ,问最少需要多少次滚动可以让原串成为目标串?
思路:假设当前要让第i位密码还原,我们可以同时转动
[i,i+1,i+2],[i,i+1],[i]
,不同的转动方式会影响后序的转动,那么可以枚举
(i,i+1,i+2)
三个密码的转动情况来进行记忆化搜索。
设
dp(i,a,b,c)
表示当前已经还原了前
i−1
个密码,且第
i
个密码的值是
AC代码
#include <cstdio>
#include <cmath>
#include <cctype>
#include <bitset>
#include <algorithm>
#include <cstring>
#include <utility>
#include <string>
#include <iostream>
#include <map>
#include <set>
#include <vector>
#include <queue>
#include <stack>
using namespace std;
#pragma comment(linker, "/STACK:1024000000,1024000000")
#define eps 1e-10
#define inf 0x3f3f3f3f
#define PI pair<int, int>
typedef long long LL;
const int maxn = 1000 + 5;
int dp[maxn][10][10][10];
char st[maxn], ed[maxn];
int s[maxn], g[maxn], n;
int dfs(int cur, int a, int b, int c) {
if(cur >= n) return 0;
int& ans = dp[cur][a][b][c];
if(ans != -1) return ans;
ans = inf;
int step;
//向上旋转
if(a <= g[cur]) step = g[cur]-a;
else step = g[cur]+10-a;
for(int i = 0; i <= step; ++i)
for(int j = 0; j <= i; ++j) {
ans = min(ans, dfs(cur+1, (b+i)%10, (c+j)%10, s[cur+3]) + step);
}
//向下旋转
if(a <= g[cur]) step = a+10-g[cur];
else step = a-g[cur];
for(int i = 0; i <= step; ++i)
for(int j = 0; j <= i; ++j) {
ans = min(ans, dfs(cur+1, (b-i+10)%10, (c-j+10)%10, s[cur+3]) + step);
}
return ans;
}
int main() {
while(scanf("%s %s", st, ed) == 2) {
memset(dp, -1, sizeof(dp));
n = strlen(st);
for(int i = 0; i < n; ++i) {
s[i] = st[i]-'0';
g[i] = ed[i]-'0';
}
s[n] = s[n+1] = g[n] = g[n+1] = '0';
printf("%d\n", dfs(0, s[0], s[1], s[2]));
}
return 0;
}
如有不当之处欢迎指出!