算法提高 密码锁 (BFS)

问题描述
你获得了一个据说是古代玛雅人制作的箱子。你非常想打开箱子看看里面有什么东西,但是不幸的是,正如所有故事里一样,神秘的箱子出现的时候总是会挂着神秘的锁。
这个锁上面看起来有  N  个数字,它们排成一排,并且每个数字都在  0  到  2  之间。你发现你可以通过锁上的机关来交换相邻两个数字的顺序。比如,如果原来有  5  个数字  02120,在一次交换以后你就可以得到  20120,01220,02210  或者  02102。
根据你所搜集的情报,这个锁在上面存在某连续四个数字是“2012”的时候会自动打开。现在,你需要计算一下,你至少需要进行多少次交换操作才能打开这把锁?
输入格式
输入数据的第一行有一个正整数  N。(4  ≤  N  ≤  13)  输入数据的第二行有  N  个数字  a1,a2,  ...,  aN  ,其中  ai  表示这个锁上面第  i  个数字的值,满足  0  ≤  ai  ≤  2。这些数字之间没有空格分隔。
输出格式
你只需要输出一个数字,即你至少需要的交换次数。如果无论如何都没有希望打开这把锁,输出  -1。
样例输入
5
02120
样例输出
1
对样例的解释
把前两个数字交换以后,锁上的数字是  20120,其中存在连续四个数字2,  0,  1,  2,因此锁会打开。

 

思路: 对于开锁是当且仅当给定的的n个数字中出现2012,我们可以将这几个数字当成字符串输入 因为是求最小交换次数并且是每一次是一种新的状态所以我们很容易想到用BFS

将每一次交换后的新的状态和当前的交换次数加入队列,每一次判断是否达到开锁的状态;

注意: 因为每一次的交换可能会出现相同的状态 所以我们在交换之后要注意 首先:如果即将交换的=两个数字不同才进行交换,其次 如果交换后的状态已经出现过了就不用入队了

好了话不多说 直接上代码吧

#include<iostream>
#include<algorithm>
#include<queue>
#include<string>
#include<set>

using namespace std;
typedef pair<string,int> P;
queue<P>Q;set<string>S;
int n;
bool goal(string str)//判断是否有达到开锁的条件
{
    for(int i=0;i+3<str.size();i++)
        if(str[i]=='2'&&str[i+1]=='0'&&str[i+2]=='1'&&str[i+3]=='2')
            return true;
    return false;
}
void BFS()
{
    while(!Q.empty())
    {
        P q = Q.front();
        Q.pop();
        if(goal(q.first))//判断有没有达到可以开锁的状态
        {
            cout<<q.second<<endl;
            return;
        }
        for(int i=0;i<q.first.size()-1;i++)
        {
            string str = q.first;
            if(str[i]!=str[i+1])
            {
                swap(str[i],str[i+1]);
                if(goal(str))
                {
                    cout<<q.second+1<<endl;
                    return;
                }
                if(S.count(str)!=1)//剪枝 对于已经出现过的状态就不必再继续
                {                  //存入队列 例如22010 如果在之后的的交换中
                    S.insert(str); //再一次出现22010 则不需要再对它进行交换
                    P p;           //S 是set类型 用于记录每一种情况有没有出现过
                    p.first = str; //没有则将其加入队列 加入集合
                    p.second = q.second+1;
                    Q.push(p);
                }
            }
        }

    }
    cout<<"-1"<<endl;
}
int main()
{
    string str;
    P p;
    while(cin>>n>>str)
    {
        S.clear();
        while(!Q.empty())
            Q.pop();
        p.first = str;
        p.second = 0;
        Q.push(p);
        BFS();
    }

    return 0;
}

 

转载于:https://www.cnblogs.com/wangrunhu/p/8504722.html

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值