小srf的游戏(单调队列+dp)

【题目描述】

srf和qtc在一个规模为 1 × n 的棋盘中下棋。规则是:

第一个人可以下在第1 到 m 中的任意一个位置。接下来每一个人可以下在第 i + 1 到 i + m 的任意一个位置,其中i为上一个人下棋的位置。
每个格子里有一个数,如果一个人下棋在格子i,会得到a[i]的分值。
当不能继续操作时,结束。

小srf请你帮他算一下,当他和qtc都采取最优策略时,他的得分减去qtc的得分。

【输入】

第一行:两个正整数n和m,用空格隔开。
第二行:n 个数,表示棋盘上的数字。

【输出】

两行,每行各一个数, 第一个数为srf先手时的答案, 第二个数为qtc先手时的答案。

【输入样例1】

1 3
1

【输出样例1】

1
-1

【样例说明】

对于30% 的数据,n ≤ 15;
对于60% 的数据,m ≤ 100;
对于100% 的数据,1 ≤ n ≤ 100000,1 ≤ m,a数组中的数保证在int范围内。

思路:

从后往前dp,就能保证当前的值,肯定是后面最优的值产生的,所以当前格子的动态转移方程就是找当前格子后面的,取某一个格子满足要求(取最大值或最小值的)情况

其中找最值的操作,是通过单调队列实现的,可简化一层循环

ac代码:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <queue>
#include <cstring>
#include <set>
#include <map>
#include <sstream>
#define LL long long
#define mem(f, x) memset(f,x,sizeof(f)) 
#define fo(i,a,n) for(int i=a;i<n;++i)
#define fo2(i,a,n) for(int i=a;i<=n;++i)
using namespace std;
template<class T>inline void read(T &x){
    x=0; char c=getchar(); bool f=0;
    while(!isdigit(c))f^=c=='-',c=getchar();
    while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
    if(f)x=-x;
}
const int N=1e5+10;
int n,m;
LL a[N],dp[N][2],q[N],p[N],h1,t1,h2,t2;
void fresh_max(int i){
	//你后手的最优策略(对应我减你的得分)取max
	while(h1<=t1&&dp[q[t1]+1][1]+a[q[t1]]<=dp[i+1][1]+a[i])t1--;
}
void fresh_min(int i){
	//我后手的最优策略(对应你减我的得分)max
	//等价于我后手的最优策略(对应我减你的得分)min
	while(h2<=t2&&dp[p[t2]+1][0]-a[p[t2]]>=dp[i+1][0]-a[i])t2--;
}
void init(){
	read(n);read(m);
    fo2(i,1,n)read(a[i]);
    q[h1=t1=0]=n;
	p[h2=t2=0]=n;
} 
int main(){
    init();
    for(register int i=n;i>=1;i--){
        fresh_max(i);q[++t1]=i;
        fresh_min(i);p[++t2]=i;
        
        while(h1<=t1&&q[h1]>=i+m)h1++;
        //我先手的最优策略(对应我减你的得分)=我先手的max-你后手的最优策略(对应你减我的得分)
        //你后手的最优策略(对应你减我的得分)=-你后手的最优策略(对应我减你的得分)
        //等价于我先手的最优策略(对应我减你的得分)=我先手的max+你后手的最优策略(对应我减你的得分)
        dp[i][0]=a[q[h1]]+dp[q[h1]+1][1];
        
        while(h2<=t2&&p[h2]>=i+m)h2++;
        //你先手的最优策略(对应我减你的得分)=我后手的最优策略(对应我减你的得分)-你先手的max
        dp[i][1]=dp[p[h2]+1][0]-a[p[h2]];
    }
    cout<<dp[1][0]<<endl<<dp[1][1];
    return 0;
}

借鉴博客:

1.xc

2.「20181024模拟」Solution

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值