[复习]深度优先搜索

深度优先搜索(dfs)是利用递归完成的以搜索深度优先的搜索

通常大概是这样的:

 1 void search(int vi){
 2     if( 达到目标 ){ //边界 
 3         if( 不满足要求 ) return ;
 4         //(和最优解比较)
 5         //当比当前最优解更优时
 6         //更新当前最优解
 7         return ;//必须返回 
 8     }
 9     for( int i = ...; i <= ... ; i++ ){ //枚举 
10         //保存结果
11         search(vi);
12         //清除刚刚保存的结果 
13     } 
14 } 

特点:

1.内存消耗小(不像广搜需要保存节点数)

2.题目的数据范围较小(例如noip普及组某年的一道题“子矩阵”)

3.耗时较长(函数的调用和返回会耗时,盲目地去枚举所有情况)

4.无法处理深度不能确定的题(例如vijos 埃及分数)

 上面提到了耗时较长的问题,这对noip通常是1s限时,貌似会有几组数据会超时,

但是经常是有一些不可能是最优解的内容,计算机却只是一个忠实执行者,

原封不动地照着代码运行。

 比如部落卫队(可以去搜搜这道题),如果现在已经选了的人再加上所有还没有

考虑的人都还是比最优解的人数少,就一定不能更新最优解,这时候就需要返回,

不再进行盲目的搜索。

剪枝:

 剪枝就是指的是把不可能成为最优解的“枝干”减去,虽说说起看起很高端的样子

事实上大多数情况,只用一个return语句和一个if语句就可以完成这项工作

  if( 不可能更新最优解 ) return ;

剪枝还有一些注意事项:

1.正确性

  如果剪枝不正确的话虽然可能速度提升了很多,但是会导致最终的答案出错

2.必要性

  有些地方去判断剪枝实际上会浪费更多的时间开销,即使这样剪枝有时候也

可能并没有对时间复杂度有显著的降低。

  例如noip的Mayan游戏,虽然当游戏中只剩1、2个方块时就没有必要继续搜索,

但是遇到这种情况比较少,而且深度也比较低(最高为5),如果用vertor,也没有

浪费过多的时间,这样剪枝只是稍微提高了一下速度,但在每次去统计也会耗费较

多的时间

 

下面附上用动态规划 + 深搜完成的“子矩阵”

Code:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #define _min(a,b) (a<b)?(a):(b)
 5 #define _abs(a) (a<0)?(0-(a)):(a)
 6 using namespace std;
 7 typedef bool boolean;
 8 //FILE *fin =fopen("submatrix.in","r");
 9 //FILE *fout=fopen("submatrix.out","w"); 
10 int map[18][18];
11 int n,m;    //行 列 
12 int r,c;    //行 列 
13 int *lines;
14 int *rows;
15 long long minx = 10000000;            //最优解 
16 long long getSubMatrix(int line,int newLine){    //计算新增加的值 
17     long long result = 0;
18     for(int i=0;i<r-1;i++){
19         result += _abs(map[rows[i]][newLine] - map[rows[i+1]][newLine]);        //行与行之间的差值 
20     }
21     if(line>0){
22         for(int i=0;i<r;i++){
23             result += _abs(map[rows[i]][newLine] - map[rows[i]][line]);//列与列之间的差值 
24         }
25     }
26     return result;
27 }
28 void getLine(/*int last,int ps,int sum*/){
29 //    if(sum>=minx) return ;
30 //    if(ps==c){
31 //        minx=sum;
32 //        return ;
33 //    }
34 //    for(int i=last+1;i<=(m-c+ps+1);i++){
35 //        lines[ps]=i;                                //保存行 
36 //        getLine(i,ps+1,sum+getSubMatrix(ps,i));
37 //    }
38     int f[18][18];
39     int s[18][18];
40     for(int i=1;i<=m;i++)
41         for(int j=1;j<=m;j++){
42             if(i==j) continue;
43             s[i][j]=getSubMatrix(i,j);
44         }
45     memset(f,0x7f,sizeof(f));
46     for(int i=1;i<=m;i++) f[1][i]=getSubMatrix(0,i);
47     for(int i=2;i<=c;i++){
48         for(int j=i;j<=m;j++){
49             for(int k=i-1;k<j;k++){
50                 f[i][j]=_min(f[i][j],f[i-1][k]+s[k][j]);
51             }
52         }
53     }
54     for(int i=1;i<=m;i++)
55         minx=_min(f[c][i],minx);
56 }
57 void getRow(int last,int ps){                    //搜索行 
58 //    if(now>n&&ps!=r) return ;                    //边界
59 //    if((ps+(n-now)+1)<r) return ;                //剪枝 
60     if(ps==r){                                    //搜索到了指定数量的行数 
61 //        getLine(0,0,0);                            //搜索列 
62         getLine();
63         return ;
64     }
65     for(int i=last+1;i<=(n-r+ps+1);i++){
66         rows[ps]=i;                                //保存行 
67         getRow(i,ps+1);
68     }
69 //    getRow(now+1,ps+1);
70 //    getRow(now+1,ps);
71 }
72 int main(){
73 //    fscanf(fin,"%d%d%d%d",&n,&m,&r,&c);
74     scanf("%d%d%d%d",&n,&m,&r,&c);
75     rows=new int[(const int)(r+1)];                //指针的初始化 
76     lines=new int[(const int)(c+1)];
77     for(int i=1;i<=n;i++){
78         for(int j=1;j<=m;j++){
79         //    fscanf(fin,"%d",&map[i][j]);        //读入数据 
80             scanf("%d",&map[i][j]);
81         }
82     }
83     getRow(0,0);                                //开始搜索 
84 //    fprintf(fout,"%ld",minx);
85     printf("%ld",minx);
86     return 0;
87 }

 

 

posted @ 2016-07-10 11:25  阿波罗2003  阅读(...)  评论(...编辑  收藏