部分和问题

部分和问题 题目:给定整数a1,a2,…,an,判断是否可以从中选出若干个数,使他们的和恰好为k。 限定条件:1<=n<=20, -10^8<=ai<=10^8, -10^8<=k<=10^8 输入:

4

1 2 4 7

13

4

1 2 4 7

15

输出:

Yes

No

//解法一:
《部分和问题》这段代码是我根据01揹包的思想做的,我把问题看成是容量为k的揹包,有n件体积和价值都为a[i]的物品,求出揹包能装的最大价值,如果最大价值正好等于揹包容量,也就是说正好有体积相加为k的策略存在,证明这n个数中存在相加之和为k的策略。不过具体能不能AC我还没有测试。
《部分和问题》感觉思路没问题,应该对O(∩_∩)O。

//代码(01揹包做法)
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;


int a[25],n,k;
int dp[100000010];


int main()
{
    while (scanf("%d",&n)!=EOF)
    {
        memset(dp,0,sizeof(dp));
        for (int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        scanf("%d",&k);
        for (int i=1;i<=n;i++)
            for (int j=k;j>=a[i];j--)
                dp[j]=max(dp[j],dp[j-a[i]]+a[i]);
        if (dp[k]==k)
            printf("Yes\n");
        else
            printf("No\n");
    }
    return 0;
}

//解法二:类似于构造一颗二叉树,i表示二叉树的层数,两支树叉分别表示加a[i]和不加a[i]的情况下sum的值。其实加与不加的策略很容易联想到01揹包中的装与不装的情况,所以用动规也能解决。

//代码:
#include<cstdio>
#include<cstring>


int a[25]={0},k,n;


//已经从前i项得到了和sum,然后对于i项之后的进行分支
//注意:第i+1项是a[i]。
bool dfs(int i,int sum)
{
    if (i==n)			//明明i是从0开始的为什么i==n结束,不应该是i==n-1吗?仔细想想,因为i==n-1时还没出界,还可以判断
        return sum==k;  	//判断sum是否等于k,递归返回条件。(这里运用的是布尔类型,这里必须用‘==’)
    if (dfs(i+1,sum))   	//不加上a[i]的情况
        return true;		//在此dfs中如果sum==k成立时就返回true
    if (dfs(i+1,sum+a[i]))	//加上a[i]的情况
        return true;		//同样,在此dfs中如果sum==k成立时就返回true
    return false;   		//无论加不加上a[i]都不能凑成k就返回false
}


void solve()
{
    if (dfs(0,0))
        printf("Yes\n");
    else
        printf("No\n");
}


int main()
{
    while (scanf("%d",&n)!=EOF)
    {
        memset(a,0,sizeof(a));
        for (int i=0;i<n;i++)
            scanf("%d",&a[i]);
        scanf("%d",&k);
        solve();
    }
    return 0;
}

点赞