Knapsack Packing(思维+multiset)

题意:

   给定一个集合A,包含2^N个数,问是否存在N个数可以组合成A。

思路:

  假设存在N个数可以组合成A。取出A中最大的两个数Firs,Second;x=First-Second,x一定位N个数中的一个数;集合A中的元素按照是否包含x可以划分为A+(包含x),A-(不包含x);且A+的大小=A-的大小=2^(N-1);

  A-可以看作是剩下的N-1个元素组合而成的集合,将A-迭代A(A=A-),继续执行上述操作,集合的大小不断变为原来的1/2,直至最终只剩下一个元素0;

  

  {

  如何不重不漏的求得所有不包含x的元素,即如何求得A-?

  首先将A中的所有元素单调不减的顺序排列,然后从小到大处理。

  序列S:不包含x的元素所形成的序列。初始为空。

  A中的第一个元素为0,将0加入S。

  假设序列S已有:a1,a2……ak,且a1+x,a2+x,……ak+x都已经从A中删除,则aK+1一定不包含x。

  证明:假设ak+1包含x

    1、若ak+1-x小于等于ak,则ai+x(1<=i<=k)未删除,与已知矛盾。

    2、若ak+1-x大于ak,则ak+1不是大于ak的第一个元素,与假设矛盾。

  固假设错误,所以ak+1不包含x。

  将ak+1加入序列S,并将aK+1+x从A中删除。重复执行加入删除的步骤,直至A为空,此时S即为A-。

  }

 

  以上为必要条件,即若存在N个元素可以组合成A,则以上条件满足。

  若A可以重复的用A-迭代,直至最后只剩下一个0,那么也存在N个元素可以组合成A。

/*
来自题解
给你n件物品和他们的重量和所有组合,求是否存在这n件物品的重量,满足这些组合
*/
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cctype>
#include <iostream>
#include <algorithm>
#include <map>
#include <set>
#include <vector>
#include <string>
#include <stack>
#include <queue>

typedef long long LL;
using namespace std;

int n, ans[25];

multiset<int> s;

int main()
{
    //freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);

    scanf("%d", &n);
    for (int i = 0, x; i < (1 << n); i++) scanf("%d", &x), s.insert(x);
    int cnt = 0;
    for (int i = 1; i <= n; i++){
        if(s.size() < 2) break;
        cnt++;
        int x = *prev(s.end()) - *prev(prev(s.end()));
        ans[i] = x;
        for (auto it = s.begin(); it != s.end(); it++){
            int y = *it;
            auto p = s.upper_bound(x + y);
            p = prev(p);
            if(p == s.end() || p == it || *p != x + y){
                printf("impossible\n");
                return 0;
            }
            s.erase(p);
        }
    }
    if(s.size() != 1 || *s.begin() != 0 || cnt < n) printf("impossible\n");
    else for (int i = 1; i <= n; i++) printf("%d\n", ans[i]);

    return 0;
}
/**/

 

 

posted @ 2020-03-19 12:13  dialectics  阅读(204)  评论(0编辑  收藏  举报