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

题面:
在这里插入图片描述
在这里插入图片描述
思路:
枚举集合s’的最小值,确定出剩下s’集合的和的范围。
∵ S ′ + S + m i n = S u m \because S' + S + min = Sum S+S+min=Sum
∵ S ′ − m i n &lt; = S \because S&#x27; - min &lt;= S Smin<=S
∵ S ′ + m i n &gt; = S \because S&#x27; + min &gt;= S S+min>=S
∴ S u m − 2 ∗ m i n + 1 2 &lt; = S ′ &lt; = S u m − m i n 2 \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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值