您的位置:首页 > 其它

UVA 10891 Game of Sum(区间DP)

2014-05-11 21:56 295 查看
这是一道博弈DP题,阳阳喜欢的博弈和我们正在学的dp混在一起了~

不过一开始看还真是有些无从下手。

可以这样想:A和B都足够聪明,所以他们每次取都必定是取最优解,且要使得对方的总和越小越好,就像是A和A自己竞争一样。那么,要能使得最后的差距越大,A每次取得的最优方法,必定是各种取法中,和最大,然后使得下一次B能取得的最优解最小,那么这样,先手A能取得最大领先。

在第i个到第j个元素间,A和B两者获取数相加为第i个到第j个元素的总和,记为sum[i][j]。记dp[i][j]是A在第i个到第j个元素区间内取最优解的总和。那么下次B能取的范围是第i到k或者第k+1到j,总之,A要在确保自己拿到最大值后,还使B能取的最优方法是k遍历i,j后能达到的最劣的最优解。

且容易得出dp[i][i] = num[i],状态转移方程为dp[i][j] = max(dp[i][j], sum - min(dp[i][k], dp[k+1][j]))。

AC代码:

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>

using namespace std;

int num[101];
int sum[101]; //sum[i]代表从1到j段的和
int dp[101][101]; //dp[i][j]代表在从i到j段能取到的最大和;
int i, j, k, len; //左端点,右端点,中间点,截取长度
int n;
int tot; //sum[j] - sum[i]

int main()
{
//        freopen("e.in", "r", stdin);

while (~scanf("%d", &n) && n)
{
sum[0] = 0;

for (i = 1; i<= n; i++)
{
scanf("%d", &num[i]);
sum[i] = sum[i-1] + num[i];
dp[i][i] = num[i];
}

for (len = 2; len <= n; len++)
{
for (i = 1, j = i + len - 1; j <= n; i++, j++)
{
tot = sum[j] - sum[i-1];
dp[i][j] = tot;
for (k = i; k < j; k++)
{
dp[i][j] = max(dp[i][j], tot - min(dp[i][k], dp[k+1][j])); //让B每次取到的最优解最小,这样A取到最优解的总和最大
}
}
}

printf("%d\n", 2 * dp[1]
- sum
); //A取得结果乘2后减去A和B共取得,即数组总和
}

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: