[luogu p3507] [POI2010]GRA-The Minima Game

Link\mathtt{Link}

P3507 [POI2010]GRA-The Minima Game - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

Description\mathtt{Description}

给定一个长度为 nn 的正整数序列,A和B轮流取数,一次取任意多,一轮一方得分为取的数的最小值,双方策略均为最大化自己的得分减对手的得分,问最后A的得分减去B的得分。

Data Range & Restrictions\mathtt{Data} \text{ } \mathtt{Range} \text{ } \mathtt{\&} \text{ } \mathtt{Restrictions}

  • 1n1061 \le n\le 10^6
  • 1ai1091 \le a_i \le 10^9

Solution\mathtt{Solution}

考虑到题目中要求最小值,而排序不会影响答案,数据范围也支持我们排序。那就先排个序。

接下来说的 aa 都是排过序的了。

先考虑双方的博弈策略:一定拿从大到小连续的一段数,也就是一定拿的是 ai,ai+1,,ana_i, a_{i +1}, \ldots, a_n 的一段数列。

为什么?

因为如果你连 aia_i 都取了,取比 aia_i 更大的数并不会影响到自己的得分(得分是选数最小值),但是不取会给对方留更多超越自己的机会。

考虑 dp。定义 fif_i 为前 ii 个数中,先手得分减去后手得分的最大值。一个很明显的方程:

fi=max(ajfj1)f_i = \max(a_j - f_{j - 1})1ji1 \le j \le i

什么意思呢?ajfj1a_j - f_{j - 1} 其实描述的就是我从 ii 取到 jj,我的分数减去你的分数。很好理解:A先从 ii 取到 jj,我的得分是 aja_j(因为 aja_jaia_i 中最小的是 aja_j),而B的最大得分自然是 fj1f_{j - 1}(在 A 从 ii 取到 jj 后,剩下的从 11j1j - 1 这一段相当于 B 为先手取数,得分 fj1f_{j - 1})。

这个做法 n2n^2,时间复杂度不是特别美丽。主要分数不美丽,不然暴力最美

方便叙述,把 ajfj1a_j - f_{j - 1} 说成式子 FjF_j.

观察到 fif_i 也就是 jj11iiFjF_j 最大值,而 jj11i1i - 1FjF_j 最大值已经被 fi1f_{i - 1} 保存了。

fif_i 相对于 fi1f_{i - 1} 只是在 jj 的取值范围拓展到了可以取到 ii 了,那么我们只需要把 fi1f_{i - 1} 和新的 FiF_i 比较一下最大值就可以了。也就是 fi=max(fi1,Fi)f_i = \max(f_{i - 1}, F_i).

到这里还能理解吗?不能理解就多看看吧!相信你可以看懂。

最后把 FjF_j 还原成原来的样子,得到最终状态转移方程:

fi=max(fi1,aifi1)f_i = \max(f_ {i - 1}, a_i - f_{i - 1})

Time Complexity\mathtt{Time} \text{ } \mathtt{Complexity}

O(nlogn)\operatorname{O}(n \log n)

因为排序。

状态转移是 nn 的复杂度的。

Code\mathtt{Code}

/*
 * @Author: crab-in-the-northeast 
 * @Date: 2022-04-06 23:23:22 
 * @Last Modified by: crab-in-the-northeast
 * @Last Modified time: 2022-04-06 23:30:39
 */
#include <iostream>
#include <cstdio>
#include <algorithm>

inline int read() {
	int x = 0;
	bool flag = true;
	char ch = getchar();
	while (ch < '0' || ch > '9') {
		if (ch == '-')
			flag = false;
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = (x << 1) + (x << 3) + ch - '0';
		ch = getchar();
	}
	if (flag)
		return x;
	return ~(x - 1);
}

const int maxn = 1000005;
int a[maxn], f[maxn];

int main() {
	int n = read();
	for (int i = 1; i <= n; ++i)
		a[i] = read();
	std :: sort(a + 1, a + 1 + n);

	f[1] = a[1];
	for (int i = 2; i <= n; ++i)
		f[i] = std :: max(f[i - 1], a[i] - f[i - 1]);
	
	printf("%d\n", f[n]);
	return 0;
}

不错的dp!

posted @   dbxxx  阅读(25)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示