2019上海网络赛 Stone Game (背包dp)

传送门
题意:n个石头,每个石头有一个重量ai, 所有石头重量之和为S, 要求从中选一些石头,被选石头重量和为S’, 满足S’ >=S-S’ 且 从S’中去掉任何一个石头,上式不成立,也就是选的石头的重量和刚好>=总重量的一半。问共有多少种选择方案。
思路: 把序列通过贪心法进行预处理,即由大至小排序,然后设计状态dp[i][j]表示前i个石头里面选取若干个,使总和为j的方案个数。这样的状态转移方程dp[i][j] = dp[i-1][j] + dp[i-1][j-a[i]]。这种题型我是第一次做,因为不能单单的只是通过最终的结果来得出答案,因为你并不知道是否满足题目所给的条件,所以很巧妙的方法就是在过程中收集答案。对于一个状态dp[i][j]而言,为了不重复计算,我们只考虑一定选第i个的情况,这样如果j >= sum-j && j-a[i] <= sum-j, 则一定选i的方案数可以包含在答案中。
这种题目还是靠经验比较多,因为一般在设计状态的时候都是想怎么直接得出答案,但却可以设计一个较为简单的状态,然后得出答案

#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<cstring>
using namespace std;
const long long MOD = 1e9+7;
long long dp[150000]; //dp[i][tot]表示考虑前i个石子,总数为tot,满足的条件数
long long a[305];
long long prefix[305];
bool cmp(long long a, long long b){
    return a > b;
}
int main(){
    int t;
    cin >> t;
    while(t--){
        memset(dp, 0, sizeof(dp));
        int n;
        cin >> n;

        long long sum = 0;
        for(int i = 1; i <= n; ++i){
            cin >> a[i];
            sum += a[i];
        }
        sort(a+1, a+1+n, cmp);

        long long ans = 0;
        dp[0] = 1;
        for(int i = 1; i <= n; ++i){
            for(int tot = sum; tot >= a[i]; --tot){
                dp[tot] = (dp[tot]+dp[tot-a[i]]) % MOD;
                if(tot >= sum-tot && tot-a[i] <= sum-tot)
                    ans = (ans + dp[tot-a[i]]) % MOD;
            }
        }

        cout << ans << endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值