666RPG(计数DP,滚动数组)

题目链接: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;
} 

你可能感兴趣的:(动态规划)