bzoj 2091 The Minima Game - 动态规划 - 博弈论
题目传送门
需要验证权限的传送门
题目大意
Alice和Bob轮流取$n$个正整数,Alice先进行操作。每次每人可以取任意多的数,得分是这一次取的所有数中的最小值。Alice和Bob都足够聪明,他们的策略都是让自己的得分减去对方的得分尽量大。问最终Alice的得分减去Bob的得分。
因为与顺序无关,所以考虑贪一下心。
因为使得分劲量大,所以肯定先把大的数取走。
又因为一次取的得分是所有取的数中的最小值,所以取走的数是排序后的连续的一段。否则对方可以取你间断的那一个数,这样显然不优。
所以用$f[i]$表示取走前$i$大后,Alice的得分减去Bob的得分,但是这样无法确定转移时谁在操作。
因此把这个游戏过程倒过来dp,用$f[i]$表示在前$i$小中进行游戏,先手减去后手的得分。
转移枚举剩下的局面就行了。
然后这样转移$O(n^{2})$。但是发现dp式子蜜汁雷同:$f[i] = \max_{j = 1}^{i - 1} \{f[j] + a_{j + 1}\}$
直接记一个东西就可以$O(1)$转移了。
时间复杂度$O(n\log n)$
Code
1 /** 2 * bzoj 3 * Problem#2091 4 * Accepted 5 * Time: 1440ms 6 * Memory: 13016k 7 */ 8 #include <bits/stdc++.h> 9 #ifndef WIN32 10 #define Auto "%lld" 11 #else 12 #define Auto "%I64d" 13 #endif 14 using namespace std; 15 16 #define ll long long 17 18 int n; 19 ll *f; 20 int *ar; 21 22 inline void init() { 23 scanf("%d", &n); 24 f = new ll[(n + 1)]; 25 ar = new int[(n + 1)]; 26 for (int i = 1; i <= n; i++) 27 scanf("%d", ar + i); 28 } 29 30 inline void solve() { 31 sort(ar + 1, ar + n + 1); 32 f[1] = ar[1]; 33 for (int i = 2; i <= n; i++) 34 f[i] = max(f[i - 1], ar[i] - f[i - 1]); 35 printf(Auto"\n", f[n]); 36 } 37 38 int main() { 39 init(); 40 solve(); 41 return 0; 42 }