码迷,mamicode.com
首页 > 其他好文 > 详细

划分问题(01背包)

时间:2021-04-22 16:10:11      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:枚举   个数   思路   sum   +=   问题   its   ace   cout   

Description

给定一个正整数的集合A={a1,a2,….,an},是否可以将其分割成两个子集合,使两个子集合的数加起来的和相等。例A = { 1, 3, 8, 4, 10} 可以分割:{1, 8, 4} 及 {3, 10}

Input

第一行集合元素个数n  n <=300 第二行n个整数

Output

如果能划分成两个集合,输出任意一个子集,否则输出“no”

Sample Input

5
1 3 8 4 10

Sample Output

3 10

解题思路

  • 本题可转化为一个容量为sum/2的背包,是否存在一组物品可以刚好装满背包的问题。
  • 用dp[i][j]来表示前i个物品是否能恰好装满容量为j的背包,即可得递推公式:
    dp[i][j] = if(dp[i-1][j] || dp[i-1][j-v[i]]) // j>=v[i]
  • 路径输出:倒序输出,若前i个物品能恰好装满j容量的背包,并且前i-1个物品不能,则说明第i个物品必须装入背包,即:
    if(j >= a[i] && dp[i][j] && !dp[i-1][j]) printf("%d ", a[i]);
    然后再让容量j-=a[i]继续输出剩余物品

代码

#include<bits/stdc++.h>

using namespace std;

int a[305];

int main () {
    int n, sum = 0;
    cin >> n;
    for(int i = 1; i <= n; ++i) {
        scanf("%d", a+i);
        sum += a[i];
    }
    if(sum%2) { // 和为奇数直接输出no
        cout << "no";
        return 0;
    }
    sum/=2;
    int dp[n+5][sum+5];
    memset(dp, 0, sizeof dp);
    dp[1][0] = 1; // 这里不能少
    dp[1][a[1]] = 1; // 同上

    for (int i = 2; i <= n; ++i) { // 枚举物品
        for (int j = 0; j <= sum; ++j) { // 枚举容量
            if(dp[i-1][j]) {
                dp[i][j] = 1;
            }
            if(j >= a[i] && dp[i-1][j-a[i]]) {
                dp[i][j] = 1;
                //cout << "debug:" << i << " " << j;
            }
        }
    }
    if(!dp[n][sum]) { // 没有找到可行方案
        printf("no");
        return 0;
    }

    // 路径输出,倒序
    for (int i = n, j = sum; i >= 1&&j>0; --i) {
        if(j >= a[i] && dp[i][j] && !dp[i-1][j]) {
            printf("%d ", a[i]);
            j -= a[i];
        }
    }
    return 0;
}

划分问题(01背包)

标签:枚举   个数   思路   sum   +=   问题   its   ace   cout   

原文地址:https://www.cnblogs.com/knightoflake/p/14686898.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!