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]);
}