一个有趣的问题

省流:NPC,别看啦!

给定 \(N\) 个数 \(A_1,\cdots ,A_N\),问可不可以把它们分成两组,使得两组的和相同。

没有数据范围。

有一个很简单的 dp 方法,\(dp_{i,x}|=dp_{i-1,x-a_i}\)。 看 \(dp_{n,\frac{sum}{2}}\) 是否为 \(1\)。时间复杂度 \(O(N\times \sum_{i=1}^{N}A_i)\)

code
#include <bits/stdc++.h>

using namespace std;

#define de(x) cout<<#x<<"="<<x<<endl

using ll = long long;

const int N = 5e2+2;
const int M = 5e5+5;

int n,sum;
int a[N];
bool dp[N][M]; 

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);

	freopen("input.txt","r",stdin);
	freopen("ans.txt","w",stdout);
	cin>>n;
	for (int i=1; i<=n; i++){
		cin>>a[i];
		sum+=a[i];
	}
	dp[0][0]=1;
	for (int i=1; i<=n; i++){
		for (int j=0; j<M; j++){
			dp[i][j]|=dp[i-1][j];
			if (j>=a[i]){
				dp[i][j]|=dp[i-1][j-a[i]];
			}
		}
	}
	if (sum%2==0 && dp[n][sum/2]){
		cout<<"YES"<<endl;
	}
	else{
		cout<<"NO"<<endl;
	}
	return 0;
}

// don't waste time!!!

但是,这个方法和 \(A\) 的值域有关,\(\max A_i\)\(\sum_{i=1}^{n} A_i\) 大的时候不会很好。

我尝试了一个随机的方法,是这样的。

每一次 \(random\ shuffle\) 一次,贪心选择。如果 \(cur\ge A_i\)\(cur\)\(\frac{sum}{2}\) 开始)就选,否则不选。成功概率是什么呢?

code
#include <bits/stdc++.h>

using namespace std;

#define de(x) cout<<#x<<"="<<x<<endl

using ll = long long;

const int N = 1e3+3;

int n;
int a[N];

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);

	freopen("input.txt","r",stdin);
	freopen("output.txt","w",stdout);
	int sum=0;
	cin>>n;
	for (int i=1; i<=n; i++){
		cin>>a[i];
		sum+=a[i];
	} 
	if (sum&1){
		cout<<"NO"<<endl;
		return 0;
	}
	sum/=2;
	for (int ti=1; ti<=50; ti++){
		random_shuffle(a+1,a+1+n);
		for (int i=1; i<=n; i++){
			if (sum>=a[i]){
				sum-=a[i];
			}
		}
		if (sum==0){
			cout<<"YES"<<endl;
			return 0;
		}
	}
	cout<<"NO"<<endl;
	return 0;
}

// don't waste time!!!

我写了一个随机数据生成器。

generator
#include <bits/stdc++.h>

using namespace std;

#define de(x) cout<<#x<<"="<<x<<endl

using ll = long long;

const int N = 500;
const int A = 1000;

mt19937 rng((int) std::chrono::steady_clock::now().time_since_epoch().count());
int rnd(int x, int y){
  return uniform_int_distribution<int>(x, y)(rng);
}

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);

	freopen("input.txt","w",stdout);
	cout<<N<<endl;
	for (int i=1; i<=N; i++){
		cout<<rnd(1,A)<<" ";
	}
	cout<<endl;
	return 0;
}

// don't waste time!!!

跑了 \(1000\) 组数据,成功率 \(84.1\%\)

如果 \(N,A\) 分别改成 \(1000,100\),成功率 \(100\%\)

有没有更好的方法?待更。

posted @ 2023-08-02 18:00  SFlyer  阅读(261)  评论(0编辑  收藏  举报