题目链接:https://ac.nowcoder.com/acm/contest/373/B
动态规划:https://blog.csdn.net/weixin_39778570/article/details/87014343
ACM题集:https://blog.csdn.net/weixin_39778570/article/details/83187443
lililalala正在玩一种有N个回合的回合制RPG游戏,初始分数为0,第 i个回合lililalala有如下两种选择。
A.将分数加上ai
B.将分数×-1
lililalala同样也很讨厌野兽数666,但是他很却喜欢数字-666。他想知道有多少种不同的方案使得
N个回合后分数变为 -666且在任何一个回合之后分数都不为666。
如果两种方案有任何一个回合选择不同,就认为这两种方案是不同的。
答案请对1e8+7取模。
---------------------------------------------------------------------------------------
简单的计数dp.
dp[i][j]代表第i个回合后分数为j的方案数
则:dp[i][j]=dp[i-1][j-ai]+dp[i-1][-j]
因为开二维内存不够,需要使用滚动数组
特判屏蔽所有来自j=666的状态转移
负下标使用偏移量搞
复杂度O(n*n*1333)
#include
#define ll long long
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;
const int N=200000,mod=1e8+7;
int dp[2][2*N],n,a[305];
void solve(){
int mid = 666*n, up=mid*2;
int now = 0, lst = 1;
dp[lst][mid] = 1;
fo(i,1,n){ // 阶段
fo(j,0,up){ // 所有状态
if(a[i]+j>=0 && dp[lst][j]){
dp[now][a[i]+j] += dp[lst][j];
if(dp[now][a[i]+j]>=mod)dp[now][a[i]+j]-=mod;
}
if(dp[lst][j]){
int fu = up-j; // 取mid对称位置 左+右=2*mid
dp[now][fu] += dp[lst][j];
if(dp[now][fu]>=mod)dp[now][fu]-=mod;
}
}
dp[now][mid+666] = 0; // 任意阶段都不能出现666,断掉
memset(dp[lst], 0, sizeof(dp[lst]));
swap(now, lst);
}
cout<<dp[lst][mid-666];
}
int main(){
scanf("%d",&n);
fo(i,1,n)scanf("%d",&a[i]);
solve();
return 0;
}