UVa 10891 Game of Sum(经典博弈区间DP)
题意:
给定n个数字,A和B可以从这串数字的两端任意选数字,一次只能从一端选取。
并且A B都尽力使自己选择的结果为最大的,可以理解成A B每一步走的都是最优的。
如果A先选择,则A B差值最大是多少。
思路:
http://hi.baidu.com/knowledgetime/item/d8ec9420a2b2f98daf48f5a4
第一次做类似的博弈题目,不过还是不难理解的。上面题解还是有一点说的不到位的。
dp[i, j]表示区间[i, j]范围内能选择的最大数。这个最大数不管是A还是B,也就是说可以当作是A B交替的。
由于区间范围是不断扩张的,所以求大区间的最大值时就要向小区间询问,此时小区间相当于是个Oracle,无所不知,只管负责问就行了。
于是对于此题A B可以从两端选取任意多值,所以当A求dp[i, j]时需要询问的区间(而且还要从两端分别)为[i, k],[k+1, j](i <= k < j)
询问的这些子区间可以当作是B所能达到的最大值,使这些B最优值达到最小,即是A的目的。
最后还要考虑一点就是:如果A把区间[i, j]全部取完,还要和上述得到的结果作个比较。
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <climits>
#include <algorithm>
using namespace std;
const int MAXN = 110;
int dp[MAXN][MAXN];
int a[MAXN], sum[MAXN];
int main()
{
int n;
while (scanf("%d", &n) && n)
{
sum[0] = 0;
for (int i = 1; i <= n; ++i)
scanf("%d", &a[i]), sum[i] = a[i] + sum[i-1];
for (int i = 1; i <= n; ++i)
dp[i][i] = a[i];
for (int p = 2; p <= n; ++p)
{
for (int i = 1, j = p; j <= n; ++i, ++j)
{
int ans = INT_MIN;
for (int k = i; k < j; ++k)
{
int m = min(dp[i][k], dp[k+1][j]);
m = sum[j] - sum[i-1] - m;
if (ans < m)
ans = m;
}
if (ans < sum[j] - sum[i-1])
ans = sum[j] - sum[i-1];
dp[i][j] = ans;
}
}
printf("%d\n", 2 * dp[1][n] - (sum[n] - sum[0]));
}
return 0;
}
-------------------------------------------------------
kedebug
Department of Computer Science and Engineering,
Shanghai Jiao Tong University
E-mail: kedebug0@gmail.com
GitHub: http://github.com/kedebug
-------------------------------------------------------