acwing-165-小猫爬山(深搜)
题意:
翰翰和达达饲养了N只小猫,这天,小猫们要去爬山。
经历了千辛万苦,小猫们终于爬上了山顶,但是疲倦的它们再也不想徒步走下山了(呜咕>_<)。
翰翰和达达只好花钱让它们坐索道下山。
索道上的缆车最大承重量为W,而N只小猫的重量分别是C1、C2……CNC1、C2……CN。
当然,每辆缆车上的小猫的重量之和不能超过W。
每租用一辆缆车,翰翰和达达就要付1美元,所以他们想知道,最少需要付多少美元才能把这N只小猫都运送下山?
解:付最少的钱让所有小猫下山,另开一个数组a[i]存当前缆车内的小猫重量,如果当前小猫可以搭乘,则继续搜索,不可以搭乘的话,就再开一个新的缆车,继续搜索,最后,对所有搜索结果取一个最小值即可。如果在搜索过程中,记录的值已经大于当前答案,那么就剪枝掉。
详细注释,见代码;
代码;
#include<bits/stdc++.h> const int maxn=2e5+10; const int inf=0x3f3f3f3f; typedef long long ll; using namespace std; int n,w; int c[maxn];//体重 int a[maxn];//搭载量,初始全部置为0 int ans; bool cmp(int a,int b) { return a>b; } void dfs(int cur,int cnt)//cur,当前猫的位置,cnt需要的车辆数 { if(cnt>=ans) return ;//剪枝,如果大于已有答案,则剪掉 if(cur==n+1)//出口 { ans=min(ans,cnt); return ; } for(int i=1; i<=cnt; i++)//对当前小猫,将其安排在cnt之前的车中 { if(a[i]+c[cur]<=w)//能装下,就继续搜索 { a[i]+=c[cur]; dfs(cur+1,cnt); a[i]-=c[cur];//最后不能忘记还原 } } a[cnt+1]=c[cur];//当前小猫装不下,则新开一个cnt dfs(cur+1,cnt+1);//继续搜索 a[cnt+1]=0;//还原初始值0 } int main() { cin>>n>>w; for(int i=1; i<=n; i++) cin>>c[i]; sort(c+1,c+1+n,cmp); ans=n; dfs(1,0); cout<<ans<<endl; //system("pause"); return 0;
acwing-167-木棒(剪枝+搜索)
题意:给一些段数不超过64的木棒,将这些木棒拼接成长度相等的木棍,,使木棍长度最小
解:
搜索问题重要的是顺序(按什么顺序来搜)
枚举每个长度,找到的第一个满足要求的长度即为答案
长度一定是要能整除总长度的
此题按照木棒一根一根的顺序来拼的;
dfs中的状态dfs(u,cur,start)
dfs(0,0,0)从第一根木棒开始,当前枚举的木棍已经拼接好的长度,起始的下标(木棍内部有顺序)。
剪枝;
1,优化搜索顺序,(一般要使决策少)。长度越长的木棒决策越少,类似于填空空;所以首先对所有木棒进行排序。以从大到小的顺序来搜。
2,避免重复。避免x+y=y+x,可以人为定序,编号小的木棒在前,编号大的木棒在后。木棍内部编号递增。
3,如果当前木棒拼接失败,下一个和当前木棒长度相等的木棒也会失败。所以,当前木棒失败,直接跳过所有长度相等的木棒。
4,如果拼第一个木棒时就失败了,那么当前方案不合法,直接return掉。如果第一个位置时,失败。假设有一个木棒可以使当前位置满足长度,那么通过掉换顺序使下边某一个木棍之中的当前木棒挪到第一个位置,也会失败,(所有拼接好的木棍等长+拼接好木棍需要用到所有木棒)。
5,最后一个木棒失败了,就一定失败。(原理等同4)。
此题有可以直接忽略的木棒长度。
代码:详细注释...
#include<bits/stdc++.h> const int maxn=70; const int inf=0x3f3f3f3f; typedef long long ll; using namespace std; int n; int stick[maxn];//木棒长度 bool st[maxn];//木棍长度 int sum,length; 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(st[i]) continue;//已经使用过,跳过 int l=stick[i]; if(cur+l<=length) { st[i]=true; if(dfs(u,cur+l,i+1)) return true; st[i]=false;//搜索完毕要还原 if(!cur) return false;//剪枝4,如果第一个木棒拼接失败 if(cur+l==length) return false;//剪枝5 int j=i;//剪枝3,跳过 相同长度的木棒 while(j<n &&stick[j]==l) j++; i=j-1; } } return false; } int main() { while(cin>>n&&n) { sum=0,length=0; for(int i=0; i<n; i++) { int l; cin>>l; stick[i]=l; if(l>50) continue; sum+=l; length=max(length,l); } sort(stick,stick+n); reverse(stick,stick+n);//排序 memset(st,false,sizeof(st)); for(int i=0; i<n; i++) { if(stick[i]>50) st[i]=true; } while(true) {//枚举长度... if(sum%length==0&&dfs(0,0,0)) { cout<<length<<endl; break; } length++; } } return 0; }