题目
给出n个正整数,从中挑选若干个,使得他们的和为m。如果存在多个,输出排序后字典序最小的一组。如果没有找到任何一组,输出"No Solution"。
输入
第一行:2个数n,m(1≤n≤30, 1≤m≤10^8) 第2-n+1行:每行1个数ai(1≤ai≤10^7)
输出
输出共1行,对应选中的数字,中间用空格分隔。如果没有找到任何一组,输出"No Solution"。
数据范围
对于50%的数据,1≤n≤20; 对于100%的数据,1≤n≤30, 1≤m≤10^8,1≤ai≤10^7。
输入样例
5 15
1
3
5
7
9
输出样例
1 5 9
样例解释
和为15的选择方法包括:1,5,9和3,5,7。1,5,9的字典序更小。
题解
深度搜索,但是因为数据量较大,需要剪枝。可以看出来:
1.当输入中最小的数都大于m时,无解。
2.当m大于所有输入的和时,也无解,可以递推到,当前剩下的数的和小于(m-当前选择数的和)时,无解。
3.因为按照大小输出,所以我们每次都从较小的数开始选择,那么当选择一个数后,下一个选择的数一定要大于当前选择的数,而且要求答案的字典序最小,所以当发现第一个解后就可以结束程序。
根据这三点剪枝编写dfs即可,为了加快第2点判断的计算,我们可以维护一个前缀和:
#include<bits/stdc++.h> using namespace std; const int maxn=39; int n,m; int a[maxn]/*输入数据存储*/,sum[maxn]/*输入排序后的前缀和*/,ans[maxn]/*答案存储*/; void dfs(int nsum,int depth,int pos) { if(depth>n)return; /*递归出口*/ if(m-nsum>sum[n]-sum[pos-1])return; /*第二点推论的无解情况*/ if(m==nsum){ /*搜索到答案*/ for(int i=0;i<depth;i++) printf("%d ",ans[i]); exit(0); } for(int i=pos;i<=n;i++){ if(nsum+a[i]<=m){ ans[depth]=a[i]; dfs(nsum+a[i],depth+1,i+1); } } } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&a[i]); sort(a+1,a+1+n); for(int i=1;i<=n;i++) /*计算前缀和*/ sum[i]=sum[i-1]+a[i]; if(a[1]<=m&&m<=sum[n]) /*判断第一点和第二点无解的情况*/ dfs(0,0,1); printf("No Solution"); return 0; }
浙公网安备 33010602011771号