题目

给出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;
} 

 

  

 

 

posted on 2020-04-14 12:09  新望  阅读(476)  评论(0)    收藏  举报