Bags Game

题目传送门

这种博弈问题挺经典的,第一时间就应该想到 区间 D P 区间DP 区间DP ,小小地积累一下吧

解法

设计出 D P DP DP
f l . r : 考虑区间 [ l , r ] . 先手可以获得的最大差值 f_{l.r} : 考虑区间 [l,r] .先手可以获得的最大差值 fl.r:考虑区间[l,r].先手可以获得的最大差值
应该有些题也可以定义为 最大值 , 计算答案时用 总和 算出 差值 , 但是这道不行,计算中途可能减去 A A A C C C 不好记录

考虑转移

  1. 直接取两端:
    显然有 f l , r ← max ⁡ ( a l − f l + 1 , r , a r − f l , r − 1 ) f_{l,r} \gets \max(a_l-f_{l+1,r},a_r-f_{l,r-1}) fl,rmax(alfl+1,r,arfl,r1)

  2. S n u k e Snuke Snuke
    f l , r ← s l , r − A / C − ( f i , i − 1 + B / D + s i , i − 1 + B / D ) f_{l,r}\gets s_{l,r}-A/C-(f_{i,i-1+B /D}+s_{i,i-1+B /D}) fl,rsl,rA/C(fi,i1+B/D+si,i1+B/D)
    用个数据结构或单调队列维护好 f i , i − 1 + B / D + s i , i − 1 + B / D f_{i,i-1+B /D}+s_{i,i-1+B /D} fi,i1+B/D+si,i1+B/D 的最小值就好了

Code

用的单调队列

#include<iostream>
#define S(p,q) s[p+q-1]-s[q-1]
#define V(p,q) (S(p,q)+f[p][q])

using ll = long long ;
using namespace std;

const int N=3e3+7;
ll f[N][N],n,A,B,C,D,s[N],x[N],q[N];
void calc(ll k,ll i,ll Z){
	for(ll j=1,h=1,t=0;j<=n+1-k;j++){
		while(h<=t&&V(k,q[t])>=V(k,j))t--;
		q[++t]=j;
		while(h<t&&q[h]<j+k-i)h++;
		if(j+k>i)f[i][j+k-i]=max(f[i][j+k-i],S(i,j+k-i)-Z-V(k,q[h]));
	}
}
int main(){
	scanf("%d %lld %lld %lld %lld",&n,&A,&B,&C,&D);
	for(ll i=1;i<=n;i++) scanf("%lld",&x[i]),s[i]=s[i-1]+x[i];
	for(ll i=1;i<=n;i++){
		for(ll j=1;j<=n+1-i;j++)f[i][j]=max(x[j]-f[i-1][j+1],x[i+j-1]-f[i-1][j]);
		calc(i-min(i,B),i,A);calc(i-min(i,D),i,C);
	}
	printf("%lld\n",f[n][1]);
}

好难呀。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值