深度优先搜索(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 }
时间: 2024-10-10 10:13:46