问题
旋转密码锁,一次至多可以旋转相邻的三个数字,最大一千位数字,可以上旋或者下旋,问从现在的数字最少需要旋转多少次才能得到想要的数字
分析
状态dp[k][a][b][c],k代表从左到右第k位数字,第0-k-1位都已经被调整好,现在调整第k,k+1,k+2位数字,a,b,c分别等于第k,k+1,k+2位数字的大小,状态dp[k][a][b][c]表示从现在的状态到把剩下的数字完全调整好需要至少多少次旋转
状态转移:
a==n2[k],这一位不用旋转,
a!=n2[k],可以上旋或者下旋,旋转格数为t,同时可以选择第k+1,k+2位也跟着旋转,第k+1位旋转的个数一定不大于第k位旋转的格数,第k+2旋转的个数一定不大于第k+1旋转的个数
调整第k位时,第k位旋转t格,第k+1旋转i格,第k+2旋转j格,由旋转方式造成的限制:j<=i<=t
因为 t是旋转第k位的总次数(第k位旋转一位,旋转两位,旋转三位次数之和)
i是旋转第k+1位的次数(第k位旋转两位,旋转三位次数之和)
j是旋转第k+2位的次数(第k位旋转三位次数之和)
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
using namespace std;
const int maxn=1005,Inf=0x3f3f3f3f;
string s1,s2;
int dp[maxn][11][11][11],n1[maxn],n2[maxn],len1;
inline int rotate(int x){
if(x>=10) return x-=10;
return x;
}
//k之前的都被调整好了,a,b,c分别代表 第k,k+1,k+2位数字 的现在状态,dp[k][a][b][c]是从当前到全部复原的最小旋转次数
int DFS(int k,int a,int b,int c){
if(k==len1) return 0;
int &ans=dp[k][a][b][c];
if(ans<Inf) return ans;
int t=0,t2=n2[k];
if(t2==a) return ans=min(ans,DFS(k+1,rotate(b),rotate(c),n1[k+3])); //相等时,不用调整这一位
//向上旋转
if(t2>a) t=t2-a; //调整第k位时,第k位旋转t格,第k+1旋转i格,第k+2旋转j格,由旋转方式造成的限制:j<=i<=t
else t=10+t2-a;
for(int i=0;i<=t;++i){
for(int j=0;j<=i;++j){
ans=min(ans,DFS(k+1,rotate(b+i),rotate(c+j),n1[k+3])+t);
}
}
//向下旋转
if(t2<a) t=a-t2;
else t=10+a-t2;
for(int i=0;i<=t;++i){
for(int j=0;j<=i;++j){
ans=min(ans,DFS(k+1,rotate(10+b-i),rotate(10+c-j),n1[k+3])+t);
}
}
return ans;
}
int main(void){
while(cin>>s1>>s2){
len1=s1.length();
for(int i=0;i<len1;++i){
n1[i]=s1[i]-'0';
n2[i]=s2[i]-'0';
}
s1[len1]=s2[len1]=0;
s1[len1+1]=s2[len1+1]=0;
for(int i=0;i<=len1;++i){
for(int j=0;j<10;++j){
for(int k=0;k<10;++k){
for(int l=0;l<10;++l) dp[i][j][k][l]=Inf;
}
}
}
printf("%d\n",DFS(0,n1[0],n1[1],n1[2]));
}
}