有這麼一類問題:枚舉從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; }