2019中秋节上海网络赛-J-Stone game(背包)

题面:
在这里插入图片描述
在这里插入图片描述
思路:
枚举集合s’的最小值,确定出剩下s’集合的和的范围。
∵S′+S+min=Sum\because S' + S + min = SumS+S+min=Sum
∵S′−min&lt;=S\because S&#x27; - min &lt;= SSmin<=S
∵S′+min&gt;=S\because S&#x27; + min &gt;= SS+min>=S
∴Sum−2∗min+12&lt;=S′&lt;=Sum−min2\therefore \frac{Sum - 2*min+1}{2} &lt;= S&#x27; &lt;= \frac{Sum - min}{2}2Sum2min+1<=S<=2Summin
上面的+1是为了排除"地板除"的影响。
所以只要确定S’区间的每个数的方案数,然后求和即可。
至于求方案数就是从n到1的背包问题。
dp[i][j]:排序后的序列,只是用从第i个到n个数从何为J的方案书2,背包问题。

#include<bits/stdc++.h>
#define per(i,a,b) for(int i=(a);i<=(b);++i)
#define rep(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;
typedef long long LL;
const int maxn = 300+10;
const int ma = 75000;
int a[maxn];
int pre[maxn];
int n = 0;
int dp[310][ma+10];
//dp[i][j]:排序后的序列,只是用从第i个到n个数从何为J的方案书2,背包问题。
int S = 0;
const int mod = 1e9 + 7;
void init(){
	S = 0;
	per(i,0,n+1){
		per(j,0,ma){
			dp[i][j] = 0;
		}
	}
}
void solve(){
	rep(i,n+1,1){
		dp[i][0] = 1;
		rep(j,ma,1){
			if(j >= a[i]){
				dp[i][j] = (dp[i+1][j] + dp[i+1][j-a[i]]) % mod;
			}else{
				dp[i][j] = dp[i+1][j]; 
			}
		}
	}
	int ans = 0;
	per(i,1,n){//枚举最小值
		if(pre[i-1] > (S-1)/2){
			break;
		}
		per(j,(S-2*a[i]+1)/2,(S-a[i])/2){//除了最小值,剩下的取出的集合的范围
			ans = (ans + dp[i+1][j]) % mod;
		}
	}
	printf("%d\n",ans);
}
int main(){
	int T = 0;
	scanf("%d",&T);
	while(T--){
		init();
		scanf("%d",&n);
		per(i,1,n){
			scanf("%d",&a[i]);
			S += a[i];
			// pre[i] = pre[i-1] + a[i];//这里的a[i]没有排序
		}
		sort(a+1,a+1+n);
		per(i,1,n){
			pre[i] = pre[i-1] + a[i];//排序之后的前缀和
		}
		solve();
	}

	return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值