必须写一个博客来出出心中这口恶气,因为每次遇到递归的问题都会卡很长时间,这次也不例外(生气的表情:哼)
题干:
蒜头君去书店买书,他有 m 元钱,书店里面有 n 本书,每本书的价格为 pi 元。蒜头君很爱学习,想把身上钱都用来买书,并且刚好买 k 本书。请帮蒜头君计算他是否能刚好用 m 元买 k 本书。
输入格式
第一行输入 3 个整数 m(1≤m≤100000000),n(1≤n≤30),k(1≤k≤min(8,n))
接下来一行输入 n 个整数,表示每本书的价格 pi(1≤pi≤100000000)。
输出格式
如果蒜头君能刚好用 m 元买 k 本书,输入一行"Yes"
, 否则输出"No"
。
样例输入1
10 4 4 1 2 3 4
样例输出1
Yes
样例输入2
10 4 3 1 2 3 4
样例输出2
No
问题分析:
咱们先想一下递归的原理:递归就是重复地做一件事,直到遇到某个条件成立时再停止递归。下面再来看这道题,这道题实质上也在重复地做一件事,即判断是否要买当前的书,如果买了那么接下来应该怎么做;
如果没买那么接下来又应该怎么做。
我写的递归函数是这个形式的:void dfs(int mon,int pos,int cnt) ,其中mon代表当前的钱数,pos代表当前正在考虑的是哪一本书(注意:是正在考虑,而不是已经买了或者没买),cnt代表当前已经买了几本书(当然不包括当前正在考虑的这本书)
注意:写法一是对的,写法二是错误的。原因是(1)式代表的是买书,所以应该减去当前这本书的价格,但是(2)式代表的是不买书,所以他不应该减去这本书的价格 (这一部分看不懂也没关系,与其它部分没有联系)
还有就是递归的结束条件要把握好,有的题的结束条件很好找,比如求阶乘问题;但有的题的条件不是不好找而是找不全,比如这道题。 所以递归的难点在于找到“重复的过程“和“结束条件”。
void dfs(int mon,int pos,int cnt) { if(mon==0 && cnt==k){ judge=true; return ; } if(mon<=0 || cnt>k){ return ; } if(pos>=n){ return ; } 写法一: dfs(mon-price[pos],pos+1,cnt+1); dfs(mon,pos+1,cnt); 写法二: mon -= price[pos]; dfs(mon,pos+1,cnt+1); (1) dfs(mon,pos+1,cnt); (2) }
代码如下:
#include<iostream> using namespace std; typedef long long LL; int n,k; LL price[35]={0}; bool judge=false; void dfs(int mon,int pos,int cnt) { if(mon==0 && cnt==k){ judge=true; return ; } if(mon<=0 || cnt>k){ return ; } if(pos>=n){ return ; } dfs(mon-price[pos],pos+1,cnt+1); dfs(mon,pos+1,cnt); } int main() { LL m; while(cin>>m>>n>>k){ judge=false; for(int i=0;i<n;i++){ cin>>price[i]; } dfs(m,0,0); if(judge){ cout<<"Yes"<<endl; } else{ cout<<"No"<<endl; } } }