The Preliminary Contest for ICPC Asia Shanghai 2019 J. Stone game 退背包

86 篇文章 0 订阅

题目链接: https://nanti.jisuanke.com/t/41420

题意:

给你 n n n 个石子的集合 S S S (以下标为标识),并告诉你每个石子重量,现在要你选取一些石子 S ′ S' S,要求满足以下条件。

s u m ( S ′ ) > s u m ( S − S ′ ) sum(S')>sum(S-S') sum(S)>sum(SS)
② 对于任意一个在 S ′ S&#x27; S 中的石子 i i i , 要求 s u m ( S ′ ) − w e i g h t [ i ] &lt; = s u m ( S − S ′ ) sum(S&#x27;)-weight[i]&lt;=sum(S-S&#x27;) sum(S)weight[i]<=sum(SS)

请给出有多少种取法使得满足上述条件。

做法:

因为石子重量是小于等于 500 500 500 的,所以我们很容易想到枚举每个最小值,但是这样的话复杂度就会是 300 ∗ 500 ∗ 150000 ∗ 500 300*500*150000*500 300500150000500 ,绝对是超了的,所以有了这么个退背包的做法,一次次把物品从中拿出,并且去看合法的重量当前的方案数(这些方案如果包括这个值一定合法,因为在枚举的总是当前合法的最小的值)。

顺便贴一下这个东西的代码

01 01 01 背包的退背包做法

for(int i=a[k];i<=sum;i++)
	dp[i]=dp[i]-dp[i-a[k]];

完全背包的退背包做法

for(int i=sum;i>=a[k];i--)
	dp[i]=dp[i]-dp[i-a[k]];

代码

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(int)a;i<=(int)b;i++)
using namespace std;
typedef long long ll;
const int maxn=150005;
const int mod=1e9+7;
ll dp[maxn],a[305],ans,sum;
int main() {
    int T,n; scanf("%d",&T);
    while(T--){
        int n; scanf("%d",&n); ans=0;
        sum=0;
        rep(i,1,n) scanf("%lld",&a[i]),sum+=a[i];
        sort(a+1,a+1+n);
        dp[0]=1;
        for(int i=1;i<=n;i++){
            for(int j=sum;j>=a[i];j--){
                dp[j]=(dp[j]+dp[j-a[i]])%mod;
            }
        }
        for(int i=1;i<=n;i++){
            for(int j=a[i];j<=sum;j++) dp[j]=(dp[j]-dp[j-a[i]]+mod)%mod;
            for(int j=0;j<=sum;j++){
                if(j+a[i]>=sum-(j+a[i])&&j<=sum-j-a[i])
                    ans=(ans+dp[j])%mod;

            }
        }
        printf("%lld\n",ans);
    }
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值