天天看點

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
");
                return 0;
            }
            s.erase(p);
        }
    }
    if(s.size() != 1 || *s.begin() != 0 || cnt < n) printf("impossible
");
    else for (int i = 1; i <= n; i++) printf("%d
", ans[i]);

    return 0;
}
/**/