【推導】【單調性】Petrozavodsk Winter Training Camp 2018 Day 1: Jagiellonian U Contest, Tuesday, January 30,

題意:有n個數,除了空集外,它們會造成2^n-1個子集,給你這些子集的和的結果,讓你還原原來的n個數。c++

假設原數是3 5 16,spa

那麼它們造成3 5 8 16 19 21 24,指針

那麼第一輪取出開頭的數(3),而後從當前最大的數(24)中減去它,而後必然會產生一個與其相等的數(21),將其一併刪去(這個過程利用單調性,使用兩個指針進行單調的從右向左的移動便可),而後將21進入下一輪的末尾……如此,3就是答案裏的數。blog

下一輪變成 5 16 21……如此重複,每次序列長度減半,獲得最終答案。get

隊友的代碼:it

#include <bits/stdc++.h>
using namespace std;
#define FOR(i,a,b) for (int i=(a);i<=(b);++i)
#define ROF(i,b,a) for (int i=(b);i>=(a);--i)
typedef long long LL;
int read(){
	int x=0,f=1; char ch=getchar();
	while (ch<'0'||ch>'9') { if (ch=='-') f=-1; ch=getchar(); }
	while (ch>='0'&&ch<='9') { x=x*10+ch-'0'; ch=getchar(); }
	return x*f;
}

const int MAXN=3000006;
queue<int> Q;
int n,m,q,a[MAXN],b[MAXN],c[MAXN];
void dfs(int x,int y){
	if (x==n+1) { if (y) c[++q]=y; return; }
	dfs(x+1,y); dfs(x+1,y+b[x]);
}
int main() {
	int T=read();
	while (T--) {
		n=read(); m=(1<<n);
		FOR(i,1,m-1) a[i]=read();
		sort(a+1,a+m);
		while (!Q.empty()) Q.pop(); 
		int flag=1;
		FOR(i,1,n) {
			b[i]=a[1]; q=0;
			int x=0,y=a[1];
			ROF(j,m-1,2) {
				if (!x)
					if (!Q.empty()) x=Q.front(); else x=0;
				if (a[j]==x) c[++q]=a[j],Q.pop(),x=0;
				else Q.push(a[j]-y);
			}
			//FOR(j,1,q) printf("%d%c",c[j]," \n"[j==q]);
			//printf("%d %d %d\n",Q.empty(),m/2-1,q);
			if (!Q.empty()||q!=m/2-1) { flag=0; break; }
			FOR(j,1,q) a[q+1-j]=c[j]; m>>=1;
		}
		if (flag)
			FOR(i,1,n) printf("%d%c",b[i]," \n"[i==n]);
		else printf("NO\n");
	}
	return 0;
}

/*
*/
相關文章
相關標籤/搜索