题目链接:点击这里
题意:给出n个数字,A和B两个人依次选中 1−k(k≥2) ,把他们合并之后自己分数加上这些数的和。求两个人在最右策略下A最多领先B多少。当数字只剩下一个之后游戏马上结束。
用 dp[i][0/1] 表示前i个数字被合并,现在是A/B先手到最后游戏结束最多领先多少。故A要最大化后继状态的值,B要最小化后继状态的值,转移就很显然了:
dp[i][0]=max{dp[j][1]+sum[j]|i<j≤n}
dp[i][1]=min{dp[j][0]+sum[j]|i<j≤n}
其中sum数组是a数组的前缀和。因为每次都用一个最大/小值去更新,所以用两个单调队列维护 dp[i][1]+sum[i] 和 dp[i][0]+sum[i] 即可。
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <vector>
#include <cstring>
#include <queue>
using namespace std;
#define maxn 200005
long long dp[maxn][2], a[maxn], sum[maxn];
int n;
priority_queue <long long> q1;
struct node {
long long x;
bool operator < (const node &a) const {
return x > a.x;
}
};
priority_queue <node> q2;
int main () {
ios::sync_with_stdio (0);
cin >> n;
sum[0] = 0;
for (int i = 1; i <= n; i++) {
cin >> a[i];
sum[i] = sum[i-1]+a[i];
}
dp[n][0] = dp[n][1] = 0;
while (!q1.empty ()) q1.pop (); while (!q2.empty ()) q2.pop ();
q1.push (dp[n][1]+sum[n]);
q2.push ((node) {dp[n][0]-sum[n]});
for (int i = n-1; i >= 1; i--) {
dp[i][0] = q1.top ();
dp[i][1] = q2.top ().x;
q1.push (dp[i][1]+sum[i]);
q2.push ((node) {dp[i][0]-sum[i]});
}
cout << dp[1][0] << endl;
return 0;
}

本文介绍了一种使用动态规划解决两人博弈问题的方法,并通过一个具体题目进行了解析。利用单调队列维护状态,实现高效的DP转移。
553

被折叠的 条评论
为什么被折叠?



