版权声明:本文为lzr原创文章,欢迎大家转载,如需转载请注明:By CQBZ LZR https://blog.csdn.net/qq_37656398/article/details/81610788
UVa 1631 Locker
题目
◇题目传送门◆(由于UVa太慢,这里提供一个vjudge的链接)
◇题目传送门(vjudge)◆
题目大意
有一个 位的数字密码锁,每位数字可以在 之间旋转。可以同时旋转一到三个。现给定初始状态和最终状态,求最少旋转次数。
思路
这道题我感觉使用递推来转移似乎很困难,所以我们使用记忆化搜索。
定义状态 为当前正在复原的位置为 ,位置 上的数为 ,位置 上的数为 ,位置 上的数为 时的最少旋转次数。
每次搜索时我们有向上和向下两种选择,所以我们要分别计算出两种情况所需的次数。
读题可发现,位置 旋转的次数是总是小于等于位置 旋转的次数;位置 旋转的次数总是小于等于位置 旋转的次数。
但是我们不知道要把位置 旋转多少次,所以我们再枚举一下旋转次数,搜索一下,就可以得出答案。
正解代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int Maxn=1000;
const int INF=0x3f3f3f3f;
int N,a[Maxn+5],b[Maxn+5];
int len;
string s1,s2;
int f[Maxn+5][10][10][10];
int DFS(int pos,int x,int y,int z) {
if(pos>=len)
return 0;
int &res=f[pos][x][y][z];
if(res!=-1)return res;
int ret=INF,t;
if(x<=b[pos])t=b[pos]-x;
else t=b[pos]+10-x;//计算旋转次数
for(int i=0;i<=t;i++)//向上旋转
for(int j=0;j<=i;j++)
ret=min(ret,DFS(pos+1,(y+i)%10,(z+j)%10,a[pos+3])+t);
if(x>=b[pos])t=x-b[pos];
else t=x+10-b[pos];
for(int i=0;i<=t;i++)//向下旋转
for(int j=0;j<=i;j++)
ret=min(ret,DFS(pos+1,(y-i+10)%10,(z-j+10)%10,a[pos+3])+t);
return res=ret;
}
int main() {
#ifdef LOACL
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
while(cin>>s1>>s2) {
len=s1.length();
for(int i=0;i<len;i++) {
a[i]=s1[i]-'0';
b[i]=s2[i]-'0';
}
a[len]=a[len+1]=b[len]=b[len+1]=0;//注意后面两位需要清空
memset(f,-1,sizeof f);//记忆化数组清空
printf("%d\n",DFS(0,a[0],a[1],a[2]));
}
return 0;
}