常见优化思路
- 优化搜索顺序
大多数情况下,优先搜索分支较少的点(排序从大到小) - 排除等效冗余
组合的形式,或者相等重复的情况 - 可行性剪枝
根据题意进行的剪枝 - 最优化剪枝
根据答案来进行剪枝 - 记忆化搜索
小猫爬山
原题连接
![这里是引用](https://img-blog.csdnimg.cn/f0a7e0c06b13474f9b8ef234f4cefbf3.png)
#include<iostream>
#include<algorithm>
#include<string.h>
#include<string>
#include<vector>
#include<queue>
#include<set>
#include<map>
#define int long long
#define fi first
#define se second
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
typedef pair<int, int>pii;
const int N = 20;
int n,m;
int a[N];
int ans=N,len=0;
int g[N];
void dfs(int u){
if(len>=ans) return;
if(u==n){
ans=min(ans,len);
return;
}
for(int i=0;i<len;i++){
if(a[u]+g[i]<=m){
g[i]=g[i]+a[u];
dfs(u+1);
g[i]=g[i]-a[u];
}
}
g[len++]+=a[u];
dfs(u+1);
g[--len]=0;
}
signed main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin>>n>>m;
for(int i=0;i<n;i++) cin>>a[i];
sort(a,a+n);
reverse(a,a+n);
dfs(0);
cout<<ans;
return 0;
}
总结
- 这种题型很常见,放到已有组或者新开一个组
- 运用了优化搜索顺序,可行性,最优化剪枝
数独
原题链接
![这里是引用](https://img-blog.csdnimg.cn/ab258c58267c414dbd42aaead1dc4358.png)
#include<iostream>
#include<algorithm>
#include<string.h>
#include<string>
#include<vector>
#include<queue>
#include<set>
#define int long long
#define fi first
#define se second
#define inf 0x3f3f3f3f
using namespace std;
typedef pair<int, int>pii;
const int N = 9,M=1<<9;
int ones[M],map[M];
int row[N],col[N],cell[3][3];
char str[100];
void init(){
for(int i=0;i<N;i++) col[i]=row[i]=(1<<N)-1;
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
cell[i][j]=(1<<N)-1;
}
void draw(int i,int j,int t,bool is_set){
if(is_set) str[i*N+j]='1'+t;
else str[i*N+j]='.';
int v=1<<t;
if(!is_set) v=-v;
row[i]-=v;
col[j]-=v;
cell[i/3][j/3]-=v;
}
int lowbit(int x){
return x&-x;
}
int get(int x,int y){
return row[x]&col[y]&cell[x/3][y/3];
}
bool dfs(int cnt){
if(!cnt) return true;
int minv=10;
int x,y;
for(int i=0;i<N;i++)
for(int j=0;j<N;j++){
if(str[i*N+j]=='.'){
int state=get(i,j);
if(ones[state]<minv){
minv=ones[state];
x=i,y=j;
}
}
}
int state=get(x,y);
for(int i=state;i;i-=lowbit(i)){
int t=map[lowbit(i)];
draw(x,y,t,true);
if(dfs(cnt-1)) return true;
draw(x,y,t,false);
}
return false;
}
signed main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
for(int i=0;i<N;i++) map[1<<i]=i;
for(int i=0;i<1<<N;i++)8
for(int j=0;j<N;j++)
ones[i]+=i>>j&1;
while(cin>>str,str[0]!='e'){
init();
int cnt=0;
for(int i=0,k=0;i<N;i++)
for(int j=0;j<N;j++,k++)
if(str[k]!='.'){
int t=str[k]-'1';
draw(i,j,t,true);
}
else cnt++;
dfs(cnt);
puts(str);
}
return 0;
}
思路总结
- 初始化ones,map,状态数组row,col,cell
- 根据字符串,完成初始状态设置,draw(i,j,t,true)
- 进行搜索,选择分支数量最少的位置,即能填数最少的点。然后依次试验可不可行,注意恢复现场(get,lowbit)
- init,draw,get,lowbit,dfs
总结
- 每个位置能填的数,取决与横列和小宫格的状态&,是1即能填这个位置的数。初始化不填每个位置均为1。状态压缩
- dfs的优化搜索顺序非常的重要
木棒
原题链接
![这里是引用](https://img-blog.csdnimg.cn/1934f07b125d41ec818a06bca9433d20.png)
#include<iostream>
#include<algorithm>
#include<string.h>
#include<string>
#include<vector>
#include<queue>
#include<set>
#include<map>
#define int long long
#define fi first
#define se second
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
typedef pair<int, int>pii;
const int N = 70;
int n;
int w[N];
int sum,length;
bool st[N];
bool dfs(int u,int cur,int start){
if(u*length==sum) return true;
if(cur==length) return dfs(u+1,0,0);
for(int i=start;i<n;i++){
if(w[i]+cur>length||st[i]) continue;
st[i]=true;
if(dfs(u,w[i]+cur,i+1)) return true;
st[i]=false;
if(!cur||cur+w[i]==length) return false;
int j=i;
while(j<n&&w[j]==w[i]) j++;
i=j-1;
}
return false;
}
signed main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
while(cin>>n,n){
memset(st,0,sizeof st);
sum=0;
for(int i=0;i<n;i++){
cin>>w[i];
sum+=w[i];
}
sort(w,w+n);
reverse(w,w+n);
length=1;
while(true){
if(sum%length==0&&dfs(0,0,0)){
cout<<length<<endl;
break;
}
length++;
}
}
return 0;
}
思路总结
- dfs当前正在凑第u根木棒,当前长度为cur,到第start根小木棍了
- 本题利用了优化搜索顺序,排除等效冗余,可行性剪枝,特别是等效冗余
总结
- 用组合数来排除等效冗余,加一个参数start
- 根据题意来排除等效冗余,第一个和最后一个能凑成length,不行直接失败
- 此类题目也非常的经典,每次从前往后扫描,凑成n个长度一样的组
生日蛋糕
原题链接
![这里是引用](https://img-blog.csdnimg.cn/c35156fb903846cdba7d3346b0e361be.png)
#include<iostream>
#include<algorithm>
#include<cmath>
#include<string.h>
#include<cstring>
#include<vector>
#include<queue>
#include<set>
#include<map>
#define int long long
#define fi first
#define se second
#define inf 1e9
using namespace std;
typedef pair<int, int>pii;
const int N = 25;
int n,m;
int minv[N],mins[N];
int R[N],H[N];
int ans=inf;
void dfs(int u,int v,int s){
if(v+minv[u]>n) return;
if(s+mins[u]>=ans) return;
if(s+2*sqrt(n-v)>=ans) return;
if(s+2*(n-v)/R[u+1]>=ans) return;
if(u==0){
if(v==n) ans=s;
return;
}
for(int r=min(R[u+1]-1,(int)sqrt((n-v)/u));r>=u;r--)
for(int h=min(H[u+1]-1,(n-v)/r/r);h>=u;h--){
int t=0;
if(u==m) t=r*r;
R[u]=r,H[u]=h;
dfs(u-1,v+r*r*h,s+2*r*h+t);
}
}
signed main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin>>n>>m;
for(int i=1;i<=m;i++){
minv[i]=minv[i-1]+i*i*i;
mins[i]=mins[i-1]+2*i*i;
}
R[m+1]=H[m+1]=inf;
dfs(m,0,0);
if(ans==inf) ans=0;
cout<<ans<<endl;
return 0;
}
思路总结
- 从下往上开始搜索合适的半径和高r,h(优化搜索顺序,从分支较少的开始搜索)
- 然后进行可行性,最优化剪枝,四种。r取最大,h取最小时,s最小
- 从大往小遍历选择这层(第u层)合适的r,h。确定赋值后进入上一层继续搜索。
总结
- 对于圆柱体积和表面积来说,如果体积固定,半径越大,高越小,表面积越小。V=rrh,S=2rh,S=2V/r,S=2sqrt(V*h)。即表面积与半径成反比,与高成“正比”。
- 每层选择合适的搜索,体会搜索的特点和本质。
![这里是引用](https://img-blog.csdnimg.cn/67874447d2314dc2af9cb2bdb2c4b0a1.jpeg)