有这么一类问题:枚举从N个整数中选择K个来知足某种条件。咱们能够在使用深度优先时,对每个元素均可以有选与不选两种方案(有的问题还能够屡次选)。
学到这招后,我会解了许多题,下面两个题我一开始就是用这种方法解得。但惋惜都不是最优解。
这种方法由于每一个元素都有两个选择,时间复杂度很高,下面举例进行说明。ios
例1: 一 得到特定数量硬币问题c++
小易准备去魔法王国采购魔法神器,购买魔法神器须要使用魔法币,可是小易如今一枚魔法币都没有,可是小易有两台魔法机器能够经过投入x(x能够为0)个魔法币产生更多的魔法币。算法
魔法机器1:若是投入x个魔法币,魔法机器会将其变为2x+1个魔法币测试
魔法机器2:若是投入x个魔法币,魔法机器会将其变为2x+2个魔法币spa
小易采购魔法神器总共须要n个魔法币,因此小易只能经过两台魔法机器产生刚好n个魔法币,小易须要你帮他设计一个投入方案使他最后刚好拥有n个魔法币。设计
输入描述: 输入包括一行,包括一个正整数n(1 ≤ n ≤ 10^9),表示小易须要的魔法币数量。code
输出描述: 输出一个字符串,每一个字符表示该次小易选取投入的魔法机器。其中只包含字符'1'和'2'。递归
输入例子1: 10ip
输出例子1: 122ci
由于每次都有两种选择,投1号箱或2号箱,个人思路以下:
#include<cstdio> #include<vector> using namespace std; vector<int> ans,temp; int n; void getMagicCoin(int sum){ if(sum>n) return; else if(sum==n){ ans=temp; } temp.push_back(1); getMagicCoin(2*sum+1); temp.pop_back(); temp.push_back(2); getMagicCoin(2*sum+2); temp.pop_back(); } int main(){ int sum=0; scanf("%d",&n); getMagicCoin(sum); for(int i=0;i<ans.size();i++){ printf("%d",ans[i]); } return 0; }
没问题,可是这种算法很暴力,极可能超时。
分析一下,能够获得以下的方案,就没有反复地递归,很好。
#include<iostream> #include<string> #include<algorithm> using namespace std; int main(){ int n; cin>>n; string ans; while(n>=1){ if(n%2==0){ n=(n-2)/2; ans+='2'; }else{ n=(n-1)/2; ans+='1'; } } reverse(ans.begin(),ans.end()); cout<<ans<<endl; return 0; }
例2:PAT甲级 1045 Favorite Color Stripe (30 分)
我又是用选与不选和DFS作的,虽然还作了剪枝,但仍是有两个测试点超时,以下:
//此方法有两个测试点超时 #include<iostream> #include<vector> using namespace std; const int maxn=10010; const int maxm=210; int tripe[maxn]; int order[maxm]; bool like[maxm]; vector<int> temp,ans; int cnt,maxL=0; //idx:将要收集的颜色 //lastLikeLevel:上一个收集的颜色的喜欢程度 void DFS(int idx,int lastLikeLevel){ if(idx==cnt){ if(temp.size()>maxL){ maxL=temp.size(); ans=temp; } return; } if(temp.size()+cnt-idx<maxL){//剪枝 return; } if(order[tripe[idx]]>=lastLikeLevel){ temp.push_back(tripe[idx]); DFS(idx+1,order[tripe[idx]]); temp.pop_back(); DFS(idx+1,lastLikeLevel); // temp.pop_back(); }else{ DFS(idx+1,lastLikeLevel); } } int main(){ int n,m,l; cin>>n>>m; for(int i=1;i<=m;i++){ int a; cin>>a; order[a]=i; like[a]=true; } cin>>l; for(int i=0;i<l;i++){ int a; cin>>a; if(like[a]){ tripe[cnt++]=a; } } DFS(0,-1); cout<<maxL<<endl; // for(int i=0;i<ans.size();i++){ // cout<<ans[i]<<" "; // } return 0; }
其实这道题能够用动态规划来作,是要求【最长不降低子序列】,以下:
#include<iostream> #include<algorithm> using namespace std; const int maxn=10010; const int maxm=210; int ht[maxm]; int tripe[maxn],dp[maxn];//dp[i]表示以i结尾的字符的最大子串长度 int main(){ int n,m,l,x; cin>>n>>m; fill(ht,ht+maxm,-1);//刚开始写的是ht+m,一个测试点错误,很久没看到,必定要细心 for(int i=0;i<m;i++){ cin>>x; ht[x]=i; } cin>>l; int cnt=0; for(int i=0;i<l;i++){ cin>>x; if(ht[x]>=0){ tripe[cnt++]=ht[x]; } } int ans=-1; for(int i=0;i<cnt;i++){ dp[i]=1; for(int j=0;j<i;j++){ if(tripe[j]<=tripe[i]&&dp[i]<dp[j]+1){ dp[i]=dp[j]+1; } } ans=max(dp[i],ans); } cout<<ans<<endl; return 0; }
例三:PTA天梯赛 L3-001 凑零钱 (30 分)
//最后一个测试点超时 //求助 #include<algorithm> #include<iostream> #include<vector> using namespace std; const int maxn=10010; int a[maxn]; vector<int> ans,temp; int n,m; bool flg=false; void func(int idx,int sum){ if(sum>m||idx>n||flg==true){ return; }else if(sum==m){ ans=temp; flg=true; return; } temp.push_back(a[idx]); func(idx+1,sum+a[idx]); temp.pop_back(); func(idx+1,sum); } int main(){ cin>>n>>m; for(int i=0;i<n;i++){ cin>>a[i]; } sort(a,a+n); func(0,0); if(flg){ for(int i=0;i<ans.size();i++){ cout<<ans[i]; if(i<ans.size()-1) cout<<" "; else cout<<"\n"; } }else{ cout<<"No Solution\n"; } return 0; }