题解:ABC321F - #(subset sum = K) with Add and Erase

题解:ABC321F - #(subset sum = K) with Add and Erase

·题目

链接:Atcoder

链接:洛谷

·难度

算法难度:B。

思维难度:C。

调码难度:B。

综合评价:见洛谷链接。

·算法

动态规划。

·思路

状态:f[i]表示目前为止能使得和为i的取法总数。

初值:f[0]肯定是1。

转移:若是“+”操作,这样转移:

for(long long j=5000;j>=x;j--){
    f[j]=(f[j]+f[j-x]+p)%p;
}

每个状态f[j]需要添加原来状态的f[j-x],以此模拟在每个取法的基础上增加一个“x”。

注意,这里是倒着遍历,否则会出现一个值在一个取法中被添加多次,类似于01背包和完全背包的转移区别。

若是“-”操作,这样转移:

for(long long j=x;j<=5000;j++){
    f[j]=(f[j]-f[j-x]+p)%p;
}

每个状态f[j]需要删除目前状态的f[j-x],以此模拟在每个取法的基础上删除一个“x”。

注意,这里是正着遍历,否则会出现一个值在一个取法中被删除多次,类似于上面的函数反过来。

·代价

O(q*max(x))。这里max(x)当成5000算即可。

·细节

字符输入用string。

·代码

#include<bits/stdc++.h>
#define K 5500
using namespace std;
const long long p=998244353;
long long f[K]={},k=0,q=0;
//状态:f[i]表示目前为止使得和为i的方案书
int main(){
    scanf("%lld%lld",&q,&k);
    f[0]=1;
    //初始值:和为0就是啥也不选
    for(long long i=1;i<=q;i++){
        string str="";
        cin>>str;
        long long x=0;
        scanf("%lld",&x);
        if(str=="+"){
            for(long long j=5000;j>=x;j--){
                f[j]=(f[j]+f[j-x]+p)%p;
            }
            //转移:每个状态f[j]在原基础上加上转移前(避免出现一个数被多次添加的情况)的f[j-x],表示选择该物品的情况
        }else{
            for(long long j=x;j<=5000;j++){
                f[j]=(f[j]-f[j-x]+p)%p;
            }
            //转移:每个状态f[j]在原基础上减去转移后(避免出现一个数被多次删除的情况)的f[j-x],表示丢弃该物品的情况
        }
        printf("%lld\n",f[k]);
        //输出结果
    }
    return 0;
}

·注意

遍历顺序不要反。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值