zoukankan      html  css  js  c++  java
  • 广度优先搜索.

    这是一道poj1184的题目,由于求解的是最优解,所以首先想到的就是使用广度优先搜索。对于这道题目我同时使用set容器,来作为状态判重。

    /*
     *	POJ 1184 聪明的打字员 
     *	版本1 : 普通的广度搜索 ,使用set进行状态判重 
    */
    #include<iostream>
    #include<queue>
    #include<set>
    #include<ctime>
    //#include<fstream>
    #define CODE_LENGTH 6 
    using namespace std;
    
    typedef struct str{
    	
    	int step;
    	string code;
    	int pos;					//光标的位置 
    	str(){}
    	//光标的初始位置为0 
    	str(int s , string c):step(s),code(c),pos(0){}
    	//重载operator< 
    	friend bool operator<(const str& a, const str& b) {
    		
    		if(a.code < b.code )
    			return true;
    		if(a.code > b.code )
    			return false;
    			
    		if( a.pos < b.pos )
    			return true;
    		if( a.pos > b.pos)
    			return false ;
    		
    		return false ;
    	}
    }Str;
    
    typedef pair<set<Str>::iterator,bool> Pair; 
    
    queue<Str> Queue;			//bfs的队列的数据结构
    set<Str> HashSet;			//状态判重的set 
    //ofstream out("result.txt");
    static int pushNUM=0;
    
    //index :标记使用的操作
    //state:表示当前节点 
    Str changeCode(const Str& state,const int& index){			
    	
    	Str tmp = state ;
    	char c;
    	
    	//1 : swap0 2: swap1 3:up 4:down 5:left 6:right
    	switch( index ){
    		
    		case 1:	if( 0 == tmp.pos ) {}
    				else {
    					
    					c= tmp.code[0];
    					tmp.code[0]= tmp.code[tmp.pos];
    					tmp.code[tmp.pos] = c;
    				}
    				break;
    				
    		case 2: if( 5 == tmp.pos ) {}
    				else{
    					
    					c = tmp.code[5];
    					tmp.code[5]= tmp.code[tmp.pos];
    					tmp.code[ tmp.pos] = c ;
    				}
    				break;
    				//注意这里是字符不是数字 
    		case 3: if( '9' == tmp.code[ tmp.pos ] ){}
    				else{
    					
    					tmp.code[tmp.pos] += 1 ; 
    				}
    				break;
    				
    		case 4: if( '0' == tmp.code[ tmp.pos ] ){}
    				else{
    					
    					tmp.code[tmp.pos] -= 1 ; 
    				}
    				break;
    		
    		case 5: if( 0 == tmp.pos ) {}
    				else{
    					
    					tmp.pos -=1 ;
    				}
    				break;
    		case 6: if( 5 == tmp.pos ){}
    				else{
    					
    					tmp.pos += 1;
    				}
    				break;
    		default: cout<<"ERROR!"<<endl; break;
    	}
    	
    	tmp.step +=1 ;
    
    	return tmp;
    } 
    
    void bfsSearch(const Str& org,Str& dest){
    	
    	//step1: push the original node into the queue 
    	Queue.push(org);
    	HashSet.insert(org);
    	Str curr,tmp;
    	Pair check;
    	
    	while(! Queue.empty()){
    		
    		curr = Queue.front();
    		Queue.pop();
    		
    		//step2 : check wether find the target node
    		if( dest.code == curr.code){
    			
    			dest.step = curr.step ;
    			return ;
    		}
    		
    		for(int i=1; i<=6 ;i++){
    			
    			tmp = changeCode(curr,i);
    			check = HashSet.insert(tmp);
    			//检查是否为重复状态 
    			if( false == check.second )
    				continue ;
    			else{
    				Queue.push(tmp ) ;
    				pushNUM++;
    			}
    				
    		}
    	}
    	
    }
    
    int main(){
    	
    	string org,dest;
    	cin>>org>>dest;
    	
    	Str strOrg(0,org),strDest(0,dest);
    	clock_t time=clock();
    	bfsSearch( strOrg, strDest );
    	cout<<"Time consumed: "<<clock()-time <<" MS"<<endl;
    	cout<<strDest.step<<endl;
    	cout<<"pushNum= "<<pushNUM<<endl;
    //	out.close();
    	return 0;
    }

    在这里值得注意的地方如下:

     

    1、由于重复状态的条件必须满足有相同的code和光标位置,所以在str结构体中重载operator<操作时,需要同时考虑code和pos两个域,保证这两个状态的都相同时,是不会被插入到set中的。因为对于set来说,他判断是否insert时发生冲突是通过两次使用operator<对两个状态进行检测,发现得出相同的结果。如果比较对象域是string这样的标准类型的话,那么在operator<函数中不能出现等于号,即写成这样是错误的:return a.code <= b.code,因为这样的话,就不会发生冲突了

     

    2、由于没有进行其他方面的优化,所以这个程序的效率是比较差的。

     

    下面是我改用hash处理状态判重的一个程序:

    /*
     *	POJ 1184 聪明的打字员 
     *	版本2 : 使用hash进行状态判重 
    */
    #include<iostream>
    #include<queue>
    #include<set>
    #include<ctime>
    //#include<fstream>
    #define HashTableSize 262147
    using namespace std;
    
    typedef struct str{
    	
    	int step;
    	string code;
    	int pos;					//光标的位置 
    	str(){}
    	//光标的初始位置为0 
    	str(int s , string c):step(s),code(c),pos(0){}
    
    }Str,*PStr;
    
    //typedef pair<set<Str>::iterator,bool> Pair; 
    
    queue<Str> Queue;			//A*的堆数据结构 
    set<Str> HashSet;			//状态判重的set 
    PStr hashTable[HashTableSize]={NULL}; //状态判重的hash表 
    //static int conflict=0,hashNum=0;
    //ofstream out("result.txt");
    
    
    
    
    int getHashValue(const Str& state){
    	
    	string str(state.code);
    	str = str.append(1,state.pos+'0');
    	int hashValue=0 ,len= str.length()+1;
    	
    	for(int i=0; i<len ;i++)
    		hashValue = 37*hashValue+ str[i];
    		
    	hashValue %= HashTableSize ;
    	if( hashValue <0 )
    		hashValue += HashTableSize ;
    		
    	return hashValue ;
    }
    
    inline bool isEqual(const Str& a,const Str& b){
    	
    	return ((a.code==b.code) && (a.pos == b.pos ))? true : false ;
    }
    
    bool insertHashTable(const Str& state){
    	
    	int hashValue = getHashValue(state);
    
    	if( hashTable[hashValue] && !isEqual(state , *hashTable[hashValue])){
    		
    		while(hashTable[hashValue])
    			hashValue ++;
    
    	}
    	if( !hashTable[hashValue]){
    		
    		hashTable[hashValue ] = new Str();
    		*hashTable[hashValue ]=state ;
    		return true ;
    	}
    	
    	return false ;
    	
    }
    
    //index :标记使用的操作
    //state:表示当前节点 
    Str changeCode(const Str& state,const int& index){			
    	
    	Str tmp = state ;
    	char c;
    	
    	//1 : swap0 2: swap1 3:up 4:down 5:left 6:right
    	switch( index ){
    		
    		case 1:	if( 0 == tmp.pos ) {}
    				else {
    					
    					c= tmp.code[0];
    					tmp.code[0]= tmp.code[tmp.pos];
    					tmp.code[tmp.pos] = c;
    				}
    				break;
    				
    		case 2: if( 5 == tmp.pos ) {}
    				else{
    					
    					c = tmp.code[5];
    					tmp.code[5]= tmp.code[tmp.pos];
    					tmp.code[ tmp.pos] = c ;
    				}
    				break;
    				//注意这里是字符不是数字 
    		case 3: if( '9' == tmp.code[ tmp.pos ] ){}
    				else{
    					
    					tmp.code[tmp.pos] += 1 ; 
    				}
    				break;
    				
    		case 4: if( '0' == tmp.code[ tmp.pos ] ){}
    				else{
    					
    					tmp.code[tmp.pos] -= 1 ; 
    				}
    				break;
    		
    		case 5: if( 0 == tmp.pos ) {}
    				else{
    					
    					tmp.pos -=1 ;
    				}
    				break;
    		case 6: if( 5 == tmp.pos ){}
    				else{
    					
    					tmp.pos += 1;
    				}
    				break;
    		default: cout<<"ERROR!"<<endl; break;
    	}
    	
    	tmp.step +=1 ;
    
    	return tmp;
    } 
    
    void bfsSearch(const Str& org,Str& dest){
    	
    	//step1: push the original node into the queue 
    	Queue.push(org);
    	insertHashTable(org);
    	Str curr,tmp;
    	bool check;
    	
    	
    	
    	while(! Queue.empty()){
    		
    		curr = Queue.front();
    		Queue.pop();
    		
    		//step2 : check wether find the target node
    		if( dest.code == curr.code){
    			
    			dest.step = curr.step ;
    			return ;
    		}
    		
    		for(int i=1; i<=6 ;i++){
    			
    			tmp = changeCode(curr,i);
    			check = insertHashTable(tmp);
    			//检查是否为重复状态 
    			if( false == check )
    				continue ;
    			else{
    				Queue.push(tmp ) ;
    			
    			}
    				
    		}
    	}
    	
    }
    
    int main(){
    	
    	string org,dest;
    	cin>>org>>dest;
    	
    	Str strOrg(0,org),strDest(0,dest);
    	clock_t time=clock();
    	bfsSearch( strOrg, strDest );
    	cout<<"Time consumed: "<<clock()-time <<" MS"<<endl;
    	cout<<strDest.step<<endl;
    //	out.close();
    	return 0;
    }

    这里我把光标的位置直接放到code的最后一位,这样直接作为字符串来处理,方便很多。但是测试下来,性能没有太大的提高,因为需要搜索的状态实在太多,到后面hash冲突现象很严重。

     

    后来我用A*算法来优化BFS搜索,代码如下:

    /*
     *	POJ 1184 聪明的打字员 
     *	版本3 : A* ,使用set进行状态判重 
    */
    #include<iostream>
    #include<queue>
    #include<set>
    #include<ctime>
    //#include<fstream>
    #define CODE_LENGTH 6 
    #define TABLE_SIZE 1<<16
    using namespace std;
    
    typedef struct str{
    	
    	int step;
    	string code;
    	int pos;					//光标的位置 
    	int fCost;
    	int hCost;
    	int myindex;
    	int parent ;
    	
    	str(){}
    	//光标的初始位置为0 
    	str(int s , string c):step(s),code(c),pos(0){}
    	//重载operator< 
    	friend bool operator<(const str& a, const str& b) {
    		
    		//注意一定要写成< ,这样才能排除code相同的Str 
    		return a.code < b.code ;
    	}
    }Str;
    
    //堆的比较函数 
    struct compareFunction{
    	
    	bool operator()(const Str& a,const Str& b){
    		
    		return a.fCost>=b.fCost ;
    	}
    	
    };
    
    typedef pair<set<string>::iterator,bool> Pair; 
    
    priority_queue<Str,vector<Str>,compareFunction> Heap;			
    set<string> ClosedSet;					//A*的关闭列表
    set<string> OpenSet;
    
    //ofstream out("result.txt");
    static int storageTableIndex=0;
    //目标串 
    string cc="654321";
    static int pushNum=0;
    
    //index :标记使用的操作
    //state:表示当前节点 
    Str changeCode(const Str& state,const int& index){			
    	
    	Str tmp = state ;
    	char c;
    	
    	//1 : swap0 2: swap1 3:up 4:down 5:left 6:right
    	switch( index ){
    		
    		case 1:	if( 0 == tmp.pos ) {}
    				else {
    					
    					c= tmp.code[0];
    					tmp.code[0]= tmp.code[tmp.pos];
    					tmp.code[tmp.pos] = c;
    				}
    				break;
    				
    		case 2: if( 5 == tmp.pos ) {}
    				else{
    					
    					c = tmp.code[5];
    					tmp.code[5]= tmp.code[tmp.pos];
    					tmp.code[ tmp.pos] = c ;
    				}
    				break;
    				//注意这里是字符不是数字 
    		case 3: if( '9' == tmp.code[ tmp.pos ] ){}
    				else{
    					
    					tmp.code[tmp.pos] += 1 ; 
    				}
    				break;
    				
    		case 4: if( '0' == tmp.code[ tmp.pos ] ){}
    				else{
    					
    					tmp.code[tmp.pos] -= 1 ; 
    				}
    				break;
    		
    		case 5: if( 0 == tmp.pos ) {}
    				else{
    					
    					tmp.pos -=1 ;
    				}
    				break;
    		case 6: if( 5 == tmp.pos ){}
    				else{
    					
    					tmp.pos += 1;
    				}
    				break;
    		default: cout<<"ERROR!"<<endl; break;
    	}
    	
    	tmp.step +=1 ;
    
    	return tmp;
    } 
    
    //局面评估函数 
    int calCost(const string& str){
    	
    	int len= str.length(),cost=0 ;
    	for( int i=0 ; i< len ; i++){
    		
    		if( str[i]!=cc[i])
    			cost++;
    	}
    	
    	return cost;
    } 
    
    void aStarSearch(const Str& org,Str& dest){
    	
    	//step1: push the original node into the Heap
    	Heap.push(org);
    	Str curr,tmp;
    	Pair check ,check1;
    	string tmpString;
    	
    	tmpString = org.code;
    	tmpString = tmpString.append(1,org.pos+'0');
    	OpenSet.insert(tmpString);
    	
    	while(! Heap.empty()){
    		
    		curr = Heap.top();		
    		Heap.pop();
    		tmpString = curr.code;
    		tmpString = tmpString.append(1,curr.pos+'0');
    		//step2: 进入关闭列表 
    		check = ClosedSet.insert(tmpString);
    		
    		if( false == check.second )
    			continue ;
    		
    		//step3 : check wether find the target node
    		if( dest.code == curr.code){
    			
    			dest.step = curr.step ;
    			dest.parent = curr.parent;
    			return ;
    		}
    		
    		for(int i=1; i<=6 ;i++){
    			
    			tmp = changeCode(curr,i);
    			
    			tmpString = tmp.code;
    			tmpString = tmpString.append(1,tmp.pos+'0');
    			check = ClosedSet.insert(tmpString);
    			check1 = OpenSet.insert(tmpString);
    			
    			//检查是否为关闭状态,是否为已有状态 
    			if( false == check.second || false == check1.second )
    				continue ;
    				
    			else{
    				//step4: push new state into heap
    				ClosedSet.erase(check.first) ;
    				
    				tmp.hCost = curr.hCost+1 ;
    				tmp.fCost = tmp.hCost + calCost( tmp.code );
    				tmp.myindex = storageTableIndex++;
    				tmp.parent = curr.myindex ;
    //				storageTable[tmp.myindex] = tmp;
    				
    				Heap.push(tmp);
    				pushNum++;
    			}
    				
    		}
    	}
    	
    }
    
    int main(){
    	
    	string org,dest;
    	cin>>org>>dest;
    	
    	Str strOrg(0,org),strDest(0,dest);
    	strOrg.fCost=0;
    	strOrg.hCost=0;
    	strOrg.parent = -1;
    	strOrg.myindex=storageTableIndex++;
    //	storageTable[strOrg.myindex]=strOrg;
    	
    	clock_t time=clock();
    	aStarSearch( strOrg, strDest );
    	cout<<"Time consumed: "<<clock()-time <<" MS"<<endl;
    	cout<<strDest.step<<endl;
    	cout<<"pushNum= "<<pushNum<<endl;
    //	cout<<"pushNum= "<<pushNUM<<endl;
    //	out.close();
    	return 0;
    }

    这里有一些值得注意的地方:

     

    1、这里关闭列表使用的是set容器,另外和传统的A*算法不同,我这里没有将产生的已有状态,但还没有进入关闭列表的状态,加入到堆数据结构中,因为可以分析出来,在这道题目中,这些状态是不可能产生更好的搜索路径的。这样大大提高了搜索效率。

    但是在有些例子下,还是搜索时间太长了,需要进一步剪枝优化。

     

    在这些程序中使用到的一些技巧吧:

    1、使用pair这个utility。声明定义pair,如:typedef pair<set<string>::iterator , bool > Pair ;

    pair的两个域可以分别用first和second来引用。

    2、set容器中的insert有一个版本为 pair insert( constTYPE &val ); 注意返回的是pair。

    3、string中的一个append版本为:basic_string &append( size_type num, char ch );  在字符串的末尾添加num个字符ch 。

    文章转载出处:http://blog.csdn.net/urecvbnkuhbh_54245df/article/details/5853291

  • 相关阅读:
    Intel汇编语言程序设计学习-第三章 汇编语言基础-中
    Intel汇编语言程序设计学习-第三章 汇编语言基础-上
    对支付链接的加密处理 面向接口编程
    编制预算的四个好方法
    Twitter欲以10亿美元收购Flipboard 双方未置评
    分手决策——合伙人离婚时怎样保护你的公司
    领导力须突破命令控制式管理
    腾讯首席探索官建言创业者:尽可能留住早期员工
    人力资本管理的坏习惯
    我们在培训领导者这件事上行动迟缓
  • 原文地址:https://www.cnblogs.com/yangzhi/p/3576640.html
Copyright © 2011-2022 走看看