我的搜索-小结

以前写bfs、dfs都感觉这类题很弱,像什么八皇后问题,很简单的dfs,但是这个暑假做搜索题却发现,自己所学只是皮毛罢了。

遇到一道记忆化搜索直接挂,看了题解,研究了一整天才凑活搞明白,实际上呢!非常easy,算法乃需要悟之精髓,领其思想,才能以不变应万变,方能驾驭于万题之上。

首先是遇到记忆化搜索,woc看不懂啊,怎么这么高深(事实对我当时的确如此,因为还要hash)

那么记忆化搜索在我看来,是爆搜+偷懒(被众人称为DP思想跑我这儿成偷懒了),没挫,其实就是搜索,但是遇到之前搜过的情况我还费劲搜他干啥,直接拿来用啊。

此乃思想,然而算法不是用了思想就可以的,掌握一项算法或数据结构,还要能将它实现为代码。那么problem来了,我怎么知道我之前搜过,就算知道搜过,我又怎么能把原来的情况直接告诉现在的状态。如果能直接开数组那多简单,数组里面保存需要的信息就可以啦。

然而。。。往往没这么简单,即使实际状态数在空间允许范围之内,也不一定能开出合适的数组,比如9个数全排列,有9!种,但可能开数组就是九维9^9MLE,所以要能将冗余的情况剔除掉。

3种方法,在lrj的书上给出了 完美hash(即绝对的一一映射在一段连续整数内)、普通hash(需要处理冲突)、stl-set(有常数,大概经实际题目测试,用set 9秒,用hash 4秒)

其中完美hash比较难,目前仅知道 康拓展开 ,set很方便的(insert,count,clear,erase)操作能够简化代码,作为跳板保证主算法正确性。而主要要掌握的其实还是普通的hash,毕竟他的用途还是很广泛的。

其实我还是没能很好的悟懂hash算法的精髓,不过大体我在编码的时候遇到一个通项,也就是想要把状态转化成整数,然后通过取模映射到hashsize中,而这个就应该是它的hash值,而对应的hashlist一般都是存储一些需要用的东西,但也不一定,这个地方存什么需要先思考一下。至于处理冲突,我还是喜欢开放寻址+线性补偿。

然后总结一下,bfs这个基础的搜索算法。

bfs的具体步骤,强烈建议将其作为函数来写,即使这道题弱爆了(放心以后不会遇到这么弱的题目),毕竟我发现函数可以直接return 不需要break、bool等影响代码美观的东西。需要一个队列和一个判重数组。

设置初始状态入队,当队列不为空做:检测队头(考虑出队时机,在最开始还是在最后有讲究噢,要是一开始就出队就没法后悔了如果是queue)

处理结点:考虑是否结束()扩展结点

over 这是一个很简单很简单的算法。有一种把每个队列元素当做结点的思想over

然后是dfs,回溯是什么这种名词不懂啊,管它呢。如果遇到有条件的就返回,一般写在dfs最前面,剪枝写在扩展结点的地方。注意dfs完是否要恢复状态。over

两者都是一个要注意判重的问题,反正搜索自己看着办。

A* IDA* 另外两个搜索算法。

IDA* 就是怕dfs吊死在一棵树上,又怕bfs太能等状态保存不了,于是就有了ID算法,即dfs的限制版,你不能一棵树上吊死!要有个度,实在不行咱再挖深一点,整个过程会重复之前搜过的,然而这点对于新加深的一层而言还是差多了。这就是ID算法,非常easy,只要主程序对maxd循环,dfs遇到maxd判断返回即可。那么IDA*是啥,我靠好高级的样子啊,(⊙o⊙)…然而。。。其实很扯很扯,就是ID算法的剪枝罢了。不过这个的确用到了A*算法的思想也就是需要启发式函数,什么是启发式函数,好高级,其实我也不造,不过凭感觉大概是,你这个搜索树智商好低,你看那个东西离你那么远你还浪费时间去搜那个,我启发一下你好了,于是就用启发式函数让它少搜一些不必要的东西,在我看来就是剪枝嘛。不过也是有公式的,具体是啥不记得了,大概就是h(x)+g(x)这个h(x)就是你当前深度,同dfs,bfs的step,很low的,那这个g(x)就是对于当前状态的评估函数,不过是相对于终点的,IDA*算法也就是在这个地方每道题的g函数都不同罢了。但是有一点!这个g函数一定要比实际代价小,当然最好是相等,否则就会有问题。

这里附上IDA*算法的lrj书上的题目的我的代码(这句话好绕,懒得改了)

Uva 11212

 1 #include <iostream>
 2 #include <set>
 3 #include <cstring>
 4 using namespace std;
 5 const int maxn=10;
 6 const int maxStatus=1000007;
 7 set<int>vis;
 8 int n;
 9 int hashlist[maxStatus];
10 struct Node{
11     int s[maxn];
12     int stInt(){
13         int res=0;
14         for(int i=0;i<n;i++)res=res*10+s[i];
15         return res;
16     }
17     int hash(){
18         int st=stInt();
19         int h=st%maxStatus;
20         for(int i=0;i<maxStatus;i++){
21             if(hashlist[h]==-1)break;
22             if(hashlist[h]==st)break;
23             h=(h+7)%maxStatus;
24         }
25         return h;
26     }
27 }start,goal;
28 int maxd;
29 bool dfs(Node u ,int dep){
30     if(dep==maxd){
31         if(u.stInt()==goal.stInt())return true;
32         return false;
33     }
34     int res=0;
35     for(int i=0;i<n-1;i++)if(u.s[i]!=u.s[i+1]-1)res++;
36     if(res>(maxd-dep)*3)return false;
37     for(int i=0;i<n;i++)
38         for(int j=i;j<n;j++)
39             for(int k=0;k+j-i<n;k++){
40                 Node v;
41                 for(int x=0;x<=j-i;x++)v.s[k+x]=u.s[i+x];
42                 int poiU=0,poiV=0;
43                 while(poiU<n){
44                     while(poiU>=i&&poiU<=j)poiU++;
45                     while(poiV>=k&&poiV<=k+j-i)poiV++;
46                     if(poiU<n)v.s[poiV++]=u.s[poiU++];
47                 }
48                 int stInt=v.stInt();
49                 int h=v.hash();
50                 if(hashlist[h]==stInt)continue;
51                 hashlist[h]=stInt;
52                 if(dfs(v,dep+1))return true;
53                 hashlist[h]=-1;
54             }
55     return false;
56 }
57 int main(){
58     for(int i=0;i<10;i++)goal.s[i]=i;
59     int t=0;
60     while(cin>>n&&n){
61         for(int i=0;i<n;i++){
62             cin>>start.s[i];
63             start.s[i]--;
64         }
65         for(maxd=0;;maxd++){
66             memset(hashlist,-1,sizeof(hashlist));
67             if(dfs(start,0))break;
68         }
69         cout<<"Case "<<++t<<": "<<maxd<<endl;
70     }
71     return 0;
72 }

对于变量名的命名,这里我还要提一下,竞赛中变量名要命得尽量简短但又能较直接表达其意义,更重要的是不能出现编译ambiguous的错误,这是很致命的,一旦出现ambiguous的编译错误会直接导致这道题0分,然而ambiguous的编译问题不一定会在本机上体现出来,所以对于命名一些没有把握的名称的时候可以添加前缀或者后缀,当然这里的前缀和后缀最好是不具备什么意义且容易区分的。习惯也是要培养的。

同样附上IDA*算法中的典型埃及分数问题的代码,这道题看网上的代码觉得不好理解,我也是思考了很久,其实发觉自己的思路是没有错的,重要在于实现代码,当然我的代码也是没有问题的,但是还是不太相信自己,所以遇到错误的时候没有坚持是因为小bug导致的而浪费了时间。

能写出简短而又高效的代码固然是好的,但是在不损失空间时间的情况下,竞赛中更应该写自己有把握的代码,即使多写了几行也是有把握的,易调试的,尤其在考场这种高压环境下,是至关重要的。

Uva - 12558

 1 #include <iostream>
 2 #include <map>
 3 #include <cstring>
 4 using namespace std;
 5 typedef long long LL;
 6 const int maxn=1000;
 7 map<LL,bool>resNum;
 8 int maxd;
 9 LL v[maxn],ans[maxn];
10 LL small(LL a,LL b){
11     LL c=b/a;
12     while(a*c<b)c++;
13     return c;
14 }
15 LL gcd(LL a,LL b){
16     if(b==0)return a;
17     return gcd(b,a%b);
18 }
19 bool better(int dep){
20     for(;dep>=0;dep--)if(v[dep]!=ans[dep])break;
21     return !ans[dep] || v[dep]<ans[dep];
22 }
23 bool dfs(int dep,LL from,LL a,LL b){
24     from=max(from,small(a,b));
25     if(dep==maxd){
26         if(a!=1)return false;
27         if(b==from&&!resNum[b])v[dep]=b;
28         else return false;
29 //        if(b%a)return false; /////
30 //        if(b!=from)return false;
31 //        v[dep]=b/a; /////
32 //        if(resNum[v[dep]])return false;
33 //        if(v[dep]==0)cout<<a<<' '<<b<<' '<<from<<endl;
34         if(better(dep))memcpy(ans,v,sizeof(v));
35         return true;
36     }
37     bool ok=false;
38     for(int i=from;;i++){
39         if(resNum[i])continue;
40         if(b*(maxd-dep+1)<i*a)break;
41         v[dep]=i;
42         LL aa,bb;
43         aa=a*i-b;
44         bb=b*i;
45 //        if(aa<=0)continue;
46         LL g= gcd(aa,bb);
47         if(dfs(dep+1,i+1,aa/g,bb/g))ok=true;
48     }
49     return ok;
50 }
51 int main()
52 {
53     int T;
54     cin>>T;
55     for(int i=1;i<=T;i++){
56         
57         LL a,b,k;
58         cin>>a>>b>>k;
59         resNum.erase(resNum.begin(),resNum.end());
60         for(int j=0;j<k;j++){
61             LL x;
62             cin>>x;
63             resNum[x]=true;
64         }
65         for(maxd=0;;maxd++){
66 //            cout<<maxd<<':'<<endl;
67             memset(ans,0,sizeof(ans));
68             if(dfs(0,small(a,b),a,b))break;
69         }
70         cout<<"Case "<<i<<": ";
71         cout<<a<<'/'<<b<<'=';
72         for(int j=0;j<maxd;j++)cout<<"1/"<<ans[j]<<'+';
73         cout<<"1/"<<ans[maxd]<<endl;
74     }
75 }

说到c++的调试,让我很不习惯的是它不像pascal一样可以很容易的单步调试,当然可以调用gdb,不过还是很麻烦,一旦开始调试将会浪费很多时间。所以要做的是遇到错误,凭经验处理,尽量肉眼纠错,配合输出中间结果,这个能力是要培养的。而且不要过度依赖输出中间结果,这也是很浪费时间的,而且影响代码的美观。毕竟肉眼检查花的时间相对短,而且调试不一定总能发现一些细节上的错误。

然后讲一下双向BFS,这个算法凭着感觉打过一遍,而且还AC了那道题,然而我才知道原来我的那个写法是错的(无语O__O "…),双向bfs要一层一层交替地搜索,而不是一个节点一个节点之间的交替,一定要一层一层,方法其实很简单,两个函数,返回是否找到,再来一个主bfs,若找到返回步数,若没找到再往外扩,交替搜索。双向bfs的复杂度大概是原复杂度开根号,当然还是要再大一点。我在计算复杂度的时候发现了一个问题,我拿时间作为复杂度开根号了,这貌似不对,而应该拿计算次数开根号……

听说此题很经典,我发一下它的代码。

Uva - 1601

  1 #include <iostream>
  2 #include <map>
  3 #include <queue>
  4 #include <cctype>
  5 #include <string>
  6 #include <cstring>
  7 using namespace std;
  8 const int maxn=20;
  9 const int baseM[]={1,1<<8,1<<16};
 10 const int maxsize=1<<24;
 11 struct Node{
 12     int v[3][2],dist;
 13     inline int stInt(){
 14         int sum=0;
 15         for(int i=0;i<3;i++)sum+=(v[i][0]*16+v[i][1])*baseM[i];
 16         return sum;
 17     }
 18 };
 19 int Map[maxn][maxn];
 20 Node start,goal;
 21 int w,h,n;
 22 void read_data(){
 23     char c;
 24     start.dist=0; goal.dist=0;
 25     memset(start.v,0,sizeof(start.v));
 26     memset(goal.v,0,sizeof(goal.v));
 27     string line;
 28     getline(cin,line);
 29     for(int i=0;i<h;i++){
 30         getline(cin,line);
 31         for(int j=0;j<w;j++){
 32             char c=line[j];
 33             if(c=='#')Map[i][j]=0;
 34             if(c==' ')Map[i][j]=1;
 35             if(isalpha(c)){
 36                 Map[i][j]=1;
 37                 if(isupper(c))goal.v[c-'A'][0]=i,goal.v[c-'A'][1]=j;
 38                 else         start.v[c-'a'][0]=i,start.v[c-'a'][1]=j;
 39             }
 40         }
 41     }
 42 //////////////////------------------------------------------------- debug ---------
 43 /*    for(int i=0;i<h;i++){
 44         for(int j=0;j<w;j++)cout<<Map[i][j];
 45         cout<<endl;
 46     }
 47     cout<<start.v[n-1][0]<<' '<<start.v[n-1][1]<<endl;
 48     cout<<goal.v[n-1][0]<<' '<<goal.v[n-1][1]<<endl;
 49 */
 50 }
 51 const int stkindM[]={5,5*5,5*5*5};
 52 const int dir[5][2]={{1,0},{-1,0},{0,1},{0,-1},{0,0}};
 53 inline bool walk(Node u,int step,Node &v){
 54     int move[3];
 55     for(int i=0;i<n;i++){
 56         move[i]=step%5;
 57         step/=5;
 58     }
 59     v=u;
 60     for(int i=0;i<n;i++){
 61         int newx=v.v[i][0]=u.v[i][0]+dir[move[i]][0],
 62             newy=v.v[i][1]=u.v[i][1]+dir[move[i]][1];
 63 /*        if(newx<0||newx>=h||newy<0||newy>=w){
 64             cout<<"!!!";
 65             return false;
 66         }//////////////////////////////
 67 */        if(Map[newx][newy]==0)return false;
 68     }
 69     for(int i=0;i<n;i++)for(int j=i+1;j<n;j++)
 70             if(v.v[i][0]==v.v[j][0]&&v.v[i][1]==v.v[j][1]||
 71                v.v[i][0]==u.v[j][0]&&v.v[i][1]==u.v[j][1]&&
 72                v.v[j][0]==u.v[i][0]&&v.v[j][1]==u.v[i][1])return false;
 73 ///////////-------------------------------------------------------------------debug --------               
 74 /*    if(n==1){
 75         cout<<"Dir : dir move(x,y) "<<dir[move[0]][0]<<' '<<dir[move[0]][1]<<endl; /////////// 
 76     }
 77 */    return true;
 78 }
 79 Node q1[maxsize],q2[maxsize];
 80 bool vis1[maxsize],vis2[maxsize];
 81 int front1,back1,front2,back2;
 82 bool bfs_fro(int step,int &front1,int &back1){
 83     while(front1<back1){
 84         Node u=q1[front1];
 85         if(u.dist>step)return false;
 86         if(vis2[u.stInt()]){
 87 //            cout<<dist1[u.stInt()]<<' '<<dist2[u.stInt()]<<endl;
 88             return true;
 89         }
 90         Node v;
 91         for(int i=0;i<stkindM[n-1];i++)
 92             {
 93                 if(!walk(u,i,v))continue;
 94                 int stInt=v.stInt();
 95                 if(vis1[stInt])continue;
 96                 vis1[stInt]=true;
 97                 v.dist=u.dist+1;
 98                 q1[back1]=v; back1++;
 99 ///////////-------------------------------------------------------------------debug --------               
100 /*    if(n==1){
101         cout<<"Position v :(x,y) "<<v.v[0][0]<<' '<<v.v[0][1]<<endl; /////////// 
102         cout<<"stInt    v :(x,y) "<<v.stInt()<<endl;///////////
103     }                    
104 */        }
105         front1++;
106     }
107     return false;
108 }
109 bool bfs_back(int step,int &front2,int &back2){
110     while(front2<back2){
111         Node u=q2[front2];
112         if(u.dist>step)return false;
113         if(vis1[u.stInt()]){
114 //            cout<<dist1[u.stInt()]<<' '<<dist2[u.stInt()]<<endl;///////
115             return true;
116         }
117         Node v;
118         for(int i=0;i<stkindM[n-1];i++)
119         {
120                 if(!walk(u,i,v))continue;
121                 int stInt=v.stInt();
122                 if(vis2[stInt])continue;
123                 vis2[stInt]=true;
124                 v.dist=u.dist+1;
125                 q2[back2]=v;back2++;
126 ///////////-------------------------------------------------------------------debug --------               
127 /*    if(n==1){
128         cout<<"Position v :(x,y) "<<v.v[0][0]<<' '<<v.v[0][1]<<endl; /////////// 
129         cout<<"stInt    v :(x,y) "<<v.stInt()<<endl;///////////
130     }
131 */        }
132         front2++;
133     }
134     return false;
135 }
136 int solve(){
137     memset(vis1,0,sizeof(vis1));memset(vis2,0,sizeof(vis2));
138     vis1[start.stInt()]=true; vis2[goal.stInt()]=true; 
139     front1=1,back1=2;front2=1,back2=2;
140     q1[front1]=start;q2[front2]=goal;
141     int step1=0,step2=0;
142     while(front1<back1||front2<back2){
143         if(bfs_fro(step1,front1,back1))return step1+step2;
144         else step1++;
145         if(bfs_back(step2,front2,back2))return step1+step2;
146         else step2++;
147     }
148     return -1;
149 }
150 int main()
151 {
152     freopen("input.txt","r",stdin);
153     while(cin>>w>>h>>n&&w&&h&&n){
154         read_data();
155         cout<<solve()<<endl;
156     }
157     return 0;
158 }

加油!↖(^ω^)↗ 继续Fighting!

posted @ 2015-08-18 19:06  JimmyLin^_^  阅读(457)  评论(0编辑  收藏  举报