作者:冰糖鸽子
一道不错的字符串+宽搜练手题
思路
首先显而易见的是,关于一个数字能到达的数字的最大长度,咱们分别看三个操作:
- 操作1,位数不变
- 操作2,位数减少
- 操作3,位数加一,但不会超过初始数字的长度
所以可以得出结论:不管怎么变化,数字的长度都不会超过原数字的长度,也就是最大只能搜索到 99999 , 所以毫不犹豫用宽搜
而在宽搜中,第一次被搜到肯定是最佳答案,所以可以加上一个记忆化,复杂度降到 O(nm) \operatorname{O(nm )} O(nm) , 其中 m m m 是while中运行一次需要的时间,在我的程序中, m m m 约是 l 3 \operatorname{l^3} l3, l l l 是原数的位数
最多约需要 O(12500000) \operatorname{O(12500000)} O(12500000) 的时间,完全可以在 1s 内跑出来
最后提一句,因为这三个操作需要插入,删除,交换什么的,所以我用了 string
来存储数字
代码
- 细节说明:在开始把原数的答案设成 1 1 1 而不是 0 0 0 , 意味着所有可通过原数转换到的数的答案都大一,所以输出时要减一。而这样处理完后,不可通过原数转换到的数的答案在减一后就是 − 1 -1 −1 , 正好符合题目要求
// Problem: P1132 数字生成游戏
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P1132
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// Powered by CP Editor (https://github.com/cpeditor/cpeditor)
#include <bits/stdc++.h>
using namespace std;
int m,l;
string n;
map<string,int>ans;
queue<string>q;
void done()
{
string u,v,f;
int fl;
q.push(n);
while(!q.empty())
{
u=q.front();q.pop();fl=u.length();
//cout<<u<<endl;
//Step 1
for(int i=0;i<fl;i++)
{
for(int j=i+1;j<fl;j++)
{
v=u;
swap(v[i],v[j]);
if(!ans[v])
{
ans[v]=ans[u]+1;
q.push(v);
}
swap(v[i],v[j]);
}
}
//Step 2
for(int i=0;i<fl;i++)
{
v=u.substr(0,i)+u.substr(i+1);
if(!ans[v])
{
ans[v]=ans[u]+1;
q.push(v);
}
}
// Step 3*
if(u.length()<l)
{
for(int i=0;i<fl-1;i++)
{
for(char j=u[i]+1;j<u[i+1];j++)
{
v=u;
f="";
f+=j;
v.insert(i+1,f);
if(!ans[v])
{
ans[v]=ans[u]+1;
q.push(v);
}
}
}
}
}
}
int main()
{
cin>>n;l=n.length();
ans[n]=1;
done();
cin>>m;
for(int i=1;i<=m;i++)
{
cin>>n;
cout<<ans[n]-1<<endl;
}
return 0;
}
本文链接:16.数字生成游戏