Game Bundles 题解

题目链接

Game Bundles

分析

很神奇的一道题目
先想想如何计算一个集合和为60的子集个数
可以想到通过 \(DP\) 求解:
\(f[i][j]\) 为前 \(i\) 个数字,和为 \(j\) 的子集个数

\(f[i][j]+=f[i-1][j-a[i]]\)
\(f[i][a[i]]++\)
可以倒序枚举 \(j\) 滚掉 \(i\) 这一维
代码如下

void sol(){
	for(int i=1;i<=n;i++){
		for(int j=60;j>=a[i]+1;j--) f[j]+=f[j-a[i]];
		f[a[i]]++;
	}
}

易发现,对于和为 \(60\) 的子集,其中值大于 \(30\) 的数最多有一个
也就是说,若在原集合中加入一个大于 \(30\) 的数 \(a\) , 则 \(f[60]+=f[60-a]\)

然后开始乱搞,
先随机一堆 \(0\)\(60\) 的数,使其 \(f[60]\) 接近但不超过 \(m\)
然后将 \(f[1]\) ~ \(f[29]\) 从大到小排序,类似二进制从大到小选择,选择 \(f[i]\) 即加入 \(60-i\)
交上去发现 \(TLE\) 了,试几个较大的数,发现前面随的数的个数大多为上限 \(60\)
不难发现,加入的数较小 \(f[60]\) 增长速度要快,所以将随机数的上限改为 \(15\) ,然后就过了

代码

#include<bits/stdc++.h>
using namespace std;

#define int long long

const int NN=66,N=61;

int m,cnt=0,a[NN],f[NN];
struct F{int id,w;}f1[NN];//f数组的copy,用于排序 
bool cmp(F a,F b){return a.w>b.w;}

bool sol(){
	cnt=0;
	while(cnt<60&&f[60]<m){
		a[++cnt]=rand()%15+1;//随机1~15的数 
		for(int i=60;i>=a[cnt]+1;i--) f[i]+=f[i-a[cnt]];
		f[a[cnt]]++;
	}
	f[a[cnt]]--;
	for(int i=a[cnt]+1;i<=60;i++) f[i]-=f[i-a[cnt]];
	cnt--;
	//贪心选择 
	for(int i=1;i<=29;i++) f1[i].w=f[i],f1[i].id=i;
	sort(f1+1,f1+30,cmp);
	for(int i=1;i<=29;i++){
		int d=f1[i].id,al=f1[i].w;
		if(f[60]+al<=m&&cnt<60) a[++cnt]=60-d,f[60]+=al;
	}
	//判断答案 
	if(f[60]==m) return 1;
	else return 0;
} 

signed main(){
	srand(time(0));
	scanf("%lld", &m);
	while(!sol()) for(int i=1;i<=N;i++) f[i]=0,a[i]=0;
	cout<<cnt<<endl;
	for(int i=1;i<=cnt;i++) cout<<a[i]<<" ";
	return 0;
}
posted @ 2023-10-19 22:20  Idtwtei  阅读(6)  评论(0编辑  收藏  举报