清华 2012 玛雅人的密码

题目:

输入长度为N,且只含'0''1''2'的字符串(2=<N<=13),问每次移动相邻的两个数字,最少经过多少次,可使字符串含有"2012"。如果无解,输出-1。(例如02120经过一次移位,可以得到20120,01220,02210,02102,其中20120符合要求,因此输出为1。)

思路:

广度优先遍历(BFS),过程借用队列。
字符串通过1次移动产生的所有新子串,为其子结点。去除和爷爷结点以及父亲结点相同的子结点,然后其余入队。再出队,遍历。
如此循环,直到产生解。

学习体会:

1.程序中的isHave2012()函数,其实是模式匹配函数,其有改进算法,参见《数据结构》。
2.相关的模式匹配函数有:

字符匹配:在<string.h>中有char* strchr(char *s,char c):字符c在字符串s中首次出现的地址。
字符串匹配:在<string>中有int find(char *s),或int find(string s):找到,返回s在第一次出现的字符串数组下标,否则返回string::npos(这是一个很大的数)。
3.&&能连用:(i==1 && j==2 && k==3)。

#include <iostream>
#include <stdio.h>
#include <queue>
using namespace std;

//字符串结构体
struct MaYaString{
	char maya[14];      //玛雅数字字符串
	int level;          //得到该字符串所交换的次数
	int exc;            //父串下标为exc和(exc+1)的字符交换得到该串
};

//判断两字符串是否相同
bool isSame(char *a,char *b,int n);

//判断一个字符串是否含2012
bool isHave2012(char *a,int n);

//判断一个字符串是否可能移位成含有2012
bool isAble(char *a,int n);

int main(){
	int N,i,j,answer;                //N:字符串长度,answer:交换次数
	char cc;
	queue<MaYaString *> Que;         //队列,方便广度遍历
	bool has2012;                    //表示2012是否已经出现

	while(scanf("%d",&N)!=EOF){      //输入字符串长度
		answer=0;                         //初始化一些变量
		has2012=false;
		MaYaString *Node1=new MaYaString;
		MaYaString *Node2;
		Node1->level=0;
		Node1->exc=-1;

		scanf("%s",Node1->maya);          //输入数字字符串
		if(isHave2012(Node1->maya,N)){    //输入本身已经含有2012,则直接输出
				printf("0\n");
			    delete Node1;
		}
		else{                             //输入本身不含2012
            if(!isAble(Node1->maya,N)){         //输入不可能交换出2012
                printf("-1\n");
                delete Node1;
			}
			else{                               //输入可以交换出2012
                Que.push(Node1);                      //结点Node1入队
				while(!Que.empty()){                  //队不为空,则一直循环
					Node1=Que.front();                      //Node1出队
					Que.pop();
					for(i=0;i<N-1;i++){                     //循环产生Node1结点的子结点
						if(Node1->exc==i)                        //剪枝:不让生成和爷爷结点一样的串
							continue;
						Node2=new MaYaString;                    //子结点Node2初始化
						Node2->exc=i;
						Node2->level=(Node1->level)+1;
						for(j=0;j<N;j++){
							Node2->maya[j]=Node1->maya[j];
						}
						cc=Node2->maya[i];
						Node2->maya[i]=Node2->maya[i+1];
						Node2->maya[i+1]=cc;

						if(isSame(Node1->maya,Node2->maya,N)){   //剪枝:不让生成和父亲结点一样的串
							delete Node2;
							continue;
						}
						if(isHave2012(Node2->maya,N)){           //子结点Node2有2012了
							has2012=true;                              //表已产生答案,且记下答案
							answer=Node2->level;
							delete Node2;                              //释放所有空间
							while(!Que.empty()){
								Node2=Que.front();
								Que.pop();
								delete Node2;
							}
							break;
						}
						else{                                    //子结点Node2还没有2012,入队
							Que.push(Node2);
						}
					}
					delete Node1;                           //删除释放Node1
				}
				if(has2012){                          //已有答案
                    printf("%d\n",answer);                  //输出答案:交换次数
                }
			}
		}
	}
	return 0;
}

//判断两字符串是否相同
bool isSame(char *a,char *b,int n){
	int i;
	bool boo=true;
	for(i=0;i<n;i++){
		if(a[i]!=b[i]){
			boo=false;
			break;
		}
	}
	return boo;
}

//判断一个字符串是否含2012
bool isHave2012(char *a,int n){
	int i;
	bool boo=false;
	if(n<4){
		return false;
	}
	else{
		for(i=0;i<=(n-4);i++){
			if((a[i]=='2' && a[i+1]=='0') && (a[i+2]=='1' && a[i+3]=='2')){
				boo=true;
				break;
			}
		}
	}
	return boo;
}

//判断一个字符串是否可能移位成含有2012
//只要至少有2个'2',1个'0',1个'1',就可以
bool isAble(char *a,int n){
	int i,n0=0,n1=0,n2=0;
	if(n<4)
		return false;
	else{
		for(i=0;i<n;i++){
			switch(a[i]){
			case '0':n0++;break;
			case '1':n1++;break;
			case '2':n2++;
			}
		}
		if(n0>=1 && n1>=1 && n2>=2)
			return true;
		else
			return false;
	}
}








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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值