简直主要包括:
1.优化搜索顺序
- 在很多时候,不同的搜索顺序往往会产生不同形状的搜索树,就需要尽可能的优化
2.排除等效冗余
- 有的时候我们可以从不同的分支到达同一个结果,那么这样的分支显然就只需要通过一次就可以了
3.可行性剪枝
- 简单的来说就是尽可能早的发现不可能的情况并进行剪枝,也就是在判断的时候进行优化
4.最优性剪枝
- 在最优化问题的过程中,如果当前的代价已经小于目前搜索到的最优解,显然就不用继续下去了,那么我们可以停止搜索,直接回溯
5.记忆化
- 可以消耗空间来换取时间,就是记录每个状态的搜索结果,这样在遍历时就可以直接查询了
数的划分
http://ybt.ssoier.cn:8088/problem_show.php?pid=1440
这题还是挺简单的,如果不进行处理只搜索,那么运行的会很慢,但是控制好每个枚举点的上下界,就会使运行效率便快很多了。
#include<iostream>
using namespace std;
int n,m,a[205],ans=0;
void dfs(int k)
{
int i;
if(k==0) return;
if(k==m)
{
if(n>=a[k-1]) ans++;
return;
}
for(i=a[k-1];i<=n/(m-k+1);i++)//处理好上下界
{
a[k]=i;
n-=i;
dfs(k+1);
n+=i;
}
}
int main(){
cin>>n>>m;
a[0]=1;
dfs(1);
cout<<ans<<endl;
return 0;
}
生日蛋糕
http://ybt.ssoier.cn:8088/show_source.php?runid=7398406
我用了可行性和最优性剪枝来优化了搜索
- 可行性剪枝 ——搭建过程中预见无法继续搭建,将要超过体积,将不可能达到体积 3个可行性剪
- 最优性剪枝——搭建过程中发现已建好的面积已经超过目前求得的最优表面积,或建完后面积一定会超过目前最优表面积,则停止搭建
#include <iostream> #include <cmath> using namespace std; int n,m; int minArea = 1 << 30; int minA[22] = {0},minV[22] = {0}; int area = 0; int maxVJudge(int M, int r, int h); void Dfs(int v,int N,int r,int h); int main() { cin>>n,m; for(int i = 1; i <= m; i++) { minA[i] = minA[i-1] + 2 * i * i; minV[i] = minV[i-1] + i * i * i; } int MaxH = (n - minV[m-1]) / (m * n) + 1; int MaxR = sqrt((n - minV[m-1]) / m) + 1; Dfs(n,m,MaxR,MaxH); if(minArea == 1 << 30) cout<< 0 <<endl; else cout<< minArea <<endl; return 0; } int maxVJudge(int M, int r, int h) { int maxV = 0; for(int i = 0; i < M; i++) maxV += (r-i) * (r-i) *(h-i); return maxV; } void Dfs(int v,int n,int r,int h) { if(n == 0) { if(v) return; else { minArea = min(minArea,area); return; } } if(v<= 0||minV[n] > v||maxVJudge(n,r,h) < v) // 可行性剪枝 return ; if(minA[n]+area >= minArea) // 最优性剪枝 return; for(int rr = r; rr >= n; --rr) { if(n == M) area = rr * rr; for(int hh = h; hh >= n; --hh) { area += 2 * rr * hh; Dfs(v-rr*rr*hh,n-1,rr-1,hh-1); area -= 2 * rr * hh; } } }
3.小木棍
-
剪切的很多
-
#include <iostream> #include <algorithm> #include <cstring> using namespace std; int n,sum=0,a[60],len,m,temp,minn=0,used[60]; bool cmp(int x,int y) { return x>y; } void read() { cin>>n; for (int i=0;i<n;i++) { cin>>a[i]; sum+=a[i]; } sort(a,a+n,cmp); //剪枝3,从大到小排序,长木棍的灵活性差,先排长木棍 } void dfs(int k,int last,int rest) //第k根木棍,last为第k根上一节木棍编号,rest为第k根木棍还需要的长度 { if (k==m) {temp=1;return;} if (rest==0) //开始下一根木棍 { int i; for (i=0;i<n;i++) //剪枝4 if (!used[i]) {used[i]=1;break;} //找到没有用过最长的木棍作为第一节 dfs(k+1,i,len-a[i]); } for (int i=last+1;i<n;i++) //剪枝5、7,因为已经排好序了,所以它的下一个不大于它的长度 { int j; if (!used[i]&&rest>=a[i]) //木棍未被访问过,且比需要的木棍长度小 { used[i]=1; dfs(k,i,rest-a[i]); used[i]=0; //回溯 j=i; //记录当前的木棍的编号 while (i<n-1&&a[i]==a[j]) i++; //剪枝6,防止相同长度的木棍搜索多次 if (i==n-1)return ; } } } void solve() { for (int i=a[0];i<=sum;i++) //剪枝2 if (sum%i==0) //剪枝1,sum能被原木棍的长度整除 { memset(used,0,sizeof(used)); len=i; used[0]=1; temp=0; //记录有没有得到答案 m=sum/i; //木棍的根数 dfs(1,0,len-a[0]); if (temp) {cout<<len<<endl;break;} } } int main () { read(); solve(); return 0; }