• 【t008】钱币变换问题


    Time Limit: 2 second
    Memory Limit: 32 MB

    【问题描述】

        给定 2*n 个方格,将其排成一行。选择两个相邻的方格,设置为空方格,初始时不放钱币。而其余的方格共放入 n-1 枚金币和 n-1 枚银币,每个方格恰好放入1枚钱币。
       
        平移操作:相邻的两个非空方格中的钱币可以平行移动到两个相邻的空方格中,但不能改变这两枚钱币的排列顺序。
        金币变换问题要求用最少移动次数将所有金币移到所有银币的左边。
        编程任务:对于给定的 n-1 枚金币和 n-1 枚银币的初始排列,设计一个分支限界法,计算满足要求的最少移动次数。

    【输入格式】

        第一行有一个正整数 T ,表示有 T 组测试数据。每组测试数据包括两行,第一行是一个整数 n ,第二行是 n-1 枚金币,n-1 枚银币和两个空方格的初始排列,字母 a 表示金币,字母 b 表示银币,字符‘.’表示空方格。。
        输入数据保证初始排列中,两个空方格是相邻的。0 ≤ T ≤ 100,2 ≤ n ≤ 10。

    【输出格式】

        对于每组测试数据,输出一行一个整数,表示最少需要移动的次数;如果无解,请输出一行“No Solution”。

    【输入样例】

        3
        3
        abab..
        5
        abba..abab
        6
        a..babbababa
    

    【输出样例】

        No Solution
        3
        4
    

    【样例说明】

        初始:  abba..abab
        第一步:abbabaa..b
        第二步:a..abaabbb
        第三步:aaaab..bbb    
        达到目标,所有的a都在b左边。
        这就是3步的解法。
    

    【题解】

    C++党直接用map来判重就可以了。

    我用的是字典树。字典树可以作为另类的"map".就是变成访问一条路径而已。

    用链表来建树。因为只有3个字符'a','b','.',所以在结构体中只要开3个位置就好。

    然后新的指针。其下一个一定要改为NULL。不然会出错(有时候不是NULL)。

    然后输入多个字符的时候,不要用cin,而应该用scanf,然后再转成string类进行操作。

    在广搜的队列中,要记录点号的位置,不然每次都要重新找一遍,很费时。

    如果用暴力的hash去做。最后一个点会超时。

    a全部在b左边的情况可以预处理出来。

    【代码】

    #include <cstdio>
    #include <string>
    #include <iostream>
    
    using namespace std;
    
    int t,n;
    
    struct node //用来建树的指针 
    {
    	node * next[4];	
    };
    
    struct data //这是队列所记录的内容 
    {
    	string ss;
    	int kongge,step;//kongge是点号所在的位置,step是当前操作了多少步 
    };
    
    node *root1,*root2; //root1是由广搜更新出的状态。root2记录的是目标状态 
    data team[1000000];//队列开到100W 
    
    bool add_tree(node *root,string s) //把字符串s加入到某个树中(roo1 或 root2) 
    {
    	int l = s.size()-1; //我在字符串前有加一个空格,所以从1开始计数 
    	node *p;
    	p = root;
    	bool flag = true;
    	for (int i = 1;i <= l;i++)
    		{
    			int t;
    			if (s[i] == '.') //点号则为3 
    				t = 3;
    					else //否则按照字母顺序(就a和b,所以是1和2) 
    						t= s[i]-'a'+1;
    			if (p->next[t] == NULL)//如果为空 表示没有出现过 
    				{
    					flag = false; //记录没出现过 
    					node *pp; //并创建一个新的节点 
    					pp = new node;
    					for (int j = 1;j <= 3;j++)
    						pp->next[j] = NULL;
    					p->next[t] = pp;
    					p = p->next[t];	
    				}
    				else
    					p = p->next[t]; //否则就顺着路往下走 
    		}
    	return flag; //返回有没有出现过这个字符串 
    }
    
    void ex_change(char &a,char &b) //交换a和b两个字符 
    {
    	char t;
    	t = a;
    	a = b;
    	b = t;
    }
    
    bool bfs(string s,int kongge) //bfs设计成bool型,便于判断有没有解 
    {
    	int head = 0,tail=1;
    	team[1].kongge = kongge; //记录空格的位置 
    	team[1].ss = s; //这个字符串是什么 
    	team[1].step = 0; //还有步骤数 
    	while (head != tail)
    		{
    			head++;
    			string temp1 = team[head].ss;
    			int pd = team[head].kongge;
    			for (int i = 1;i <= 2*n-1;i++) //枚举需要交换哪些位置 
    				{
    					if (temp1[i] != '.' && temp1[i+1]!='.') //如果两个位置都不是点号 
    						{
    							string temp = team[head].ss;
    							ex_change(temp[i],temp[pd]);
    							ex_change(temp[i+1],temp[pd+1]); //交换 
    							if (!add_tree(root1,temp)) //如果之前没有搜到过这个字符串 
    								{
    									tail++; //把这个字符串加入到队列的尾端 
    									team[tail].kongge = i;
    									team[tail].ss = temp;
    									team[tail].step = team[head].step + 1;
    									if (add_tree(root2,temp)) //如果这是目标状态 
    										{
    											printf("%d
    ",team[tail].step);
    											return true; //输出结果,同时返回有解 
    										}
    								}
    						}
    				}	
    		}
    	return false;//返回无解 
    }
    
    void input_data()
    {
    	scanf("%d",&t);
    	for (int i = 1;i <= t;i++)
    		{
    			root1 = new node;
    			root2 = new node;
    			for (int j = 1;j <= 3;j++) //初始化根节点 
    				root1->next[j] = NULL,root2->next[j] = NULL;
    			scanf("%d",&n);	
    			char sss[100];
    			scanf("%s",sss); //用scanf输入,这样会比较快。 
    			string s,ss;
    			ss = string(sss);
    			s = " ";
    			s+= ss;
    			ss = " ";
    			for (int j = 1;j <= 2*n;j++) //这是构造出目标状态 
    				ss+='a'; //这个字母是随便写的。全是b也可以 
    			for (int j = 1;j <= 2*n-1;j++) //枚举点号在什么位置 
    				{
    					ss[j] = ss[j+1] = '.';//设为点号 
    					int numa = 0;
    					for (int k = 1;k <= 2*n;k++) //不为点号则根据numa数目设为a或b 
    						if (ss[k]!='.')
    							if (numa < n-1)
    								ss[k] = 'a',numa++;
    									else
    										ss[k] = 'b';
    					add_tree(root2,ss);//root2是目标状态的字典树 
    					ss[j] = ss[j+1] = 'a';//回溯一下。 
    				}
    			int p = s.find('.',0);//找到初始状态点号的位置。 
    			add_tree(root1,s);
    			if (add_tree(root2,s))
    				{
    					printf("0
    ");
    					continue;
    				}
    			if (!bfs(s,p))
    				printf("No Solution
    ");
    		}
    }
    
    int main()
    {
    	//freopen("F:\rush.txt","r",stdin);
    	input_data();
    	return 0;	
    }



  • 相关阅读:
    HTTP 无法注册 URL http://+:xxxxx/ServicesName/。进程不具有此命名空间的访问权限
    C语言中宏的一些特别用法
    static和const的比较和解释
    堆和栈的区别
    c++中const用法
    链表常见笔试题
    自绘实现半透明水晶按钮 .
    C++面试题
    C/C++面试题大汇总
    C++ 值传递、指针传递、引用传递详解
  • 原文地址:https://www.cnblogs.com/AWCXV/p/7632345.html
Copyright © 2020-2023  润新知