计数DP,牛客练习赛41B题: 666RPG

本文探讨了一款回合制RPG游戏中的策略问题,玩家需要通过特定操作达到分数目标-666,同时避免分数变成666。文章详细介绍了如何使用计数DP算法求解不同方案的数量,并给出了具体的实现代码,包括状态转移方程和滚动数组的使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门

lililalala正在玩一种有 N个回合的回合制RPG游戏,初始分数为0,第 i i个回合lililalala有如下两种选择。

A.将分数加上 ai ai
B.将分数 ×-1 ×-1

lililalala同样也很讨厌野兽数 666 ,但是他很却喜欢数字 -666 -。他想知道有多少种不同的方案使得N个回合后分数变为 -666且在任何一个回合之后分数都不为 666。

如果两种方案有任何一个回合选择不同,就认为这两种方案是不同的。

答案请对 10^8+7 取模。

输入描述:

输入包含两行。

第一行一个整数 N(1≤N≤300) N(1≤N≤300)。

第二行 N N个整数 a1a2a3…an(-666≤ a1a2a3…an≤666) a1a2a3…an(-666≤ a1a2a3…an≤666)。

输出描述:
输出一行一个整数–符合条件的不同方案数。

输入
3
-333 -333 -333

输出
1
说明

仅一种符合条件的方案

第一回合选择将分数×−1。分数为0

第二回合选择将分数加上-333。分数为 -333

第三回合选择将分数加上 -333。分数为 -666

输入
3
333 333 333
输出
0

输入
13
518 -643 -503 424 -76 -18 547 26 51 -647 -457 -5 329
输出
2

计数dp.
dp[i][j]代表第i个回合后分数为j的方案数
则:dp[i][j]=dp[i-1][j-ai]+dp[i-1][-j]
因为开二维内存不够,需要使用滚动数组, 同时, 避免负数的影响用dp[i][j+add] 来代表第i个回合后分数为j的方案数。因为j最小为300 * -666 这里取add = 300 * 666。
特判屏蔽所有来自j=666的状态转移

计数dp不太熟悉, 卡了很久在 dp[cnt][j+add]=dp[cnt^1] [j-a[i]+add]+dp[cnt^1][-j+add];
之前是分开写的用了+= 但这里用到了滚动数组, 前面一轮算的数会保留到这一轮, 用+= 会造成错误。


#include<iostream>
#include<cstdio>
#include<algorithm>
#include<functional>
using namespace std;

const int add = 300*666, mod = 1e8 + 7;
int a[310];
int dp[2][add * 2];				//j的范围由300 * -666 到 300 * 666 改为 0 ~ 300 * 666 * 2

int main()
{
    int n;
    scanf("%d", &n);
    for(int i = 1; i <= n; i++)
        scanf("%d", &a[i]);
    

     dp[0][add] = 1;				// 第0回合取0值得方案=数为1
     int sum = 0;
    for(int i = 1; i <= n; i++)
    {
        sum += abs(a[i]);
        int cnt = i % 2;

        for(int j = -1 * sum; j <= sum; j++)						//遍历从-sum到sum, 避免每次从极大到极小遍历
        {
            if(j == 666) dp[cnt][j + add] = 0;
            else
            {
                dp[cnt][j+add]=dp[cnt^1][j-a[i]+add]+dp[cnt^1][-j+add];			//状态转移:dp[i][j]=dp[i-1][j-ai]+dp[i-1][-j]
                dp[cnt][j + add] %= mod;															//保存答案时避免负数的影响, 加add
            }
        }
    }

    printf("%d", dp[n % 2][add - 666]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值