挑战程序设计竞赛:部分和问题

题目大意

在这里插入图片描述
在这里插入图片描述

解题思路

  • 对于每个数,我们有两种选择
    • (1): 选
    • (2): 不选
  • (1): 这道题的背包思想:
    • 定义状态: d p [ i ] [ j ] dp[i][j] dp[i][j]: 前 i i i个数字里是否能够选出和为 j j j的数字,若能,则为1,不能则为0。
    • 目标态: d p [ n ] [ k ] dp[n][k] dp[n][k].
    • 状态转移: d p [ i ] [ j ] = d p [ i − 1 ] [ j ] ∣ ∣ d p [ i − 1 ] [ j − a [ i ] ] dp[i][j] =dp[i-1][j] \quad|| \quad dp[i-1][j-a[i]] dp[i][j]=dp[i1][j]dp[i1][ja[i]]. 其中前一项对应不选该数,后一项对应选该数。
    • 更新策略: i i i从小到大更新。
    • 初始态: d p [ 0 ] [ ∗ ] = 0 , d p [ 0 ] [ 0 ] = 1 dp[0][*] = 0, dp[0][0] = 1 dp[0][]=0,dp[0][0]=1: 表示没有数时只能和为0.
    • 复杂度: O ( n 2 a i ) O(n^2a_i) O(n2ai)
    • 注意点: 因为有负数,因此需要加入一个偏移量offset
  • (2): 这道题的深度优先搜索思想:
    • 对每个物品,选或者不选,生成深度优先搜索树进行深搜。
    • 当搜到和为k的节点时,置全局标志位flag为1。
    • 深搜递归出口:
      • 已经没有数可供选择时。
      • 当前节点代表的和已经等于k。
      • 若标志位为1,则不再继续搜。
    • 复杂度: 2 n 2^n 2n.
      在这里插入图片描述
  • 比较,很明显,对这道题,深度优先搜索更占优势。

代码

  • 背包内存溢出,按 a i a_i ai最大值绝对值为 1000 1000 1000写代码:
#include<iostream>
#include<cstring>
using namespace std;

const int offset = 1000;
const int MAXN = 21;
const int MAXM = MAXN * 2 * offset + 5;
int dp[2][MAXM];
int a[MAXN];
int main()
{
    int n, k;
    int min_sum;
    int max_sum;
    while(cin >> n)
    {
        min_sum = 0;
        max_sum = 0;
        for(int i=1; i<=n; i++)
        {
            cin >> a[i];
            if(a[i] < 0)
                min_sum += a[i];
            else
                max_sum += a[i];
        }
        cin >> k;
        memset(dp, 0, sizeof(dp));
        dp[0][0+offset] = 1;
        for(int i=1; i<=n; i++)
        {
            for(int j=-min_sum; j<=max_sum; j++)
            {
                // 小于-min_sum 和 大于max_sum的情况是不可能存在的,因此只能选择不选。
                if(j-a[i] < -min_sum || j-a[i] > max_sum)
                    dp[i%2][j+offset] = dp[(i-1)%2][j+offset];
                else
                    dp[i%2][j+offset] = dp[(i-1)%2][j+offset] || dp[(i-1)%2][j+offset-a[i]];
            }
        }

        if(dp[n%2][k+offset])
            cout << "Yes" << endl;
        else
            cout << "No" << endl;
     }
}

  • 深度优先搜索代码
#include<iostream>
using namespace std;

const int MAXM = 21;
int a[MAXM];
int sum;
int k;
int n;
int flag;
void dfs(int deep)
{
    // 已经无数可选了
    if(deep == n)
    {
        flag = (sum == k);
        return;
    }

    // 该节点和等于k
    if(sum == k)
    {
        flag = 1;
        return;
    }

    // 不选该数
    dfs(deep+1);
    if(flag)
        return
    // 选该数
    sum += a[deep];
    dfs(deep+1);
    if(flag)
        return;
    sum -= a[deep];

    return;
}
int main()
{
    while(cin >> n)
    {
        for(int i=0; i<n; i++)
            cin >> a[i];
        cin >> k;
        sum = 0;
        flag = 0;
        dfs(0);
        if(flag)
            cout << "Yes" << endl;
        else
            cout << "No" << endl;
    }
    return 0;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值