UVA 10891 Game of Sum(DP)

This is a two player game. Initially there
are n integer numbers in an array and
players A and B get chance
to take them alternatively. Each player can take one or more numbers from the
left or right end of the array but cannot take from both ends at a time. He can
take as many consecutive numbers as he wants during his time. The game ends when
all numbers are taken from the array by the players. The point of each player is
calculated by the summation of the numbers, which he has taken. Each player
tries to achieve more points from other. If both players play optimally and
player A starts the game then how much more point can
player A get than player B?

Input

The input consists of a number of cases. Each case starts with a line
specifying the integer n (0 < n ≤100),
the number of elements in the array. After that, nnumbers
are given for the game. Input is terminated by a line
where n=0.

Output

For each test case, print a number, which represents the maximum difference
that the first player obtained after playing this game optimally.

题目大意:给n个数,两个人轮流取数,可以从左往右或从右往左取任意多个。两个人都希望自己的取得的数的总和尽量大,都采取最优策略,问第一个人能比第二个人取得的数多多少。

思路:很容易可以想到一个O(n^3)的DP,用dp[i][j]代表只剩下a[i..j]的数,先手可以取得的最大值,此时后手取得的最大值为sum[i..j]
- dp[i][j]。

那么状态转移方程为:dp[i][j] = max(sum[i..j], sum[i..j] - min(dp[i+1][j],
dp[i+2][j]……), sum[i..j] - min(dp[i][j - 1], dp[i, j - 2])。

输出结果为2 * dp[1][n] - sum[1..n]。

代码(0.026S):

 1 #include <iostream>
2 #include <cstdio>
3 #include <cstring>
4 #include <algorithm>
5 using namespace std;
6
7 const int MAXN = 110;
8
9 int dp[MAXN][MAXN];
10 int a[MAXN], sum[MAXN];
11 int n;
12
13 int main() {
14 while(scanf("%d", &n) != EOF && n) {
15 for(int i = 1; i <= n; ++i) scanf("%d", a + i);
16 for(int i = 1; i <= n; ++i) sum[i] = sum[i - 1] + a[i];
17 for(int k = 0; k < n; ++k) {
18 for(int i = 1; i + k <= n; ++i) {
19 int j = i + k;
20 dp[i][j] = sum[j] - sum[i - 1];
21 for(int p = i + 1; p <= j; ++p) dp[i][j] = max(dp[i][j], sum[j] - sum[i - 1] - dp[p][j]);
22 for(int p = j - 1; p >= i; --p) dp[i][j] = max(dp[i][j], sum[j] - sum[i - 1] - dp[i][p]);
23 }
24 }
25 printf("%d\n", 2 * dp[1][n] - sum[n]);
26 }
27 }

这个DP还有优化的余地,观察状态转移方程可以发现,dp[i][j]使用了min(dp[i+1][j],
dp[i+2][j]……),而dp[i+1][j]=min(dp[i+2][j], dp[i+3][j]……),有重复的部分。

于是我们可以用l[i][j]记录max(dp[i][j], dp[i+1][j],
dp[i+2][j]……),即从左往右取的后手最小值,则sum[i..j] - min(dp[i+1][j],
dp[i+2][j]……)可以写成sum[i..j]-l[i+1][j]。每次更新l[i][j] = min(dp[i][j], l[i+1][j])。

同理用r[i][j]记录从右往左取的后手最小值。

至此DP优化至O(n^2)。

代码(0.015S):

 1 #include <iostream>
2 #include <cstdio>
3 #include <cstring>
4 #include <algorithm>
5 using namespace std;
6
7 const int MAXN = 110;
8
9 int dp[MAXN][MAXN];
10 int l[MAXN][MAXN], r[MAXN][MAXN];
11 int a[MAXN], sum[MAXN];
12 int n;
13
14 int main() {
15 while(scanf("%d", &n) != EOF && n) {
16 for(int i = 1; i <= n; ++i) scanf("%d", a + i);
17 for(int i = 1; i <= n; ++i) sum[i] = sum[i - 1] + a[i];
18 for(int k = 0; k < n; ++k) {
19 for(int i = 1; i + k <= n; ++i) {
20 int j = i + k;
21 l[i][j] = r[i][j] = dp[i][j] = sum[j] - sum[i - 1];
22 if(i != j) {
23 dp[i][j] = max(dp[i][j], sum[j] - sum[i - 1] - l[i + 1][j]);
24 dp[i][j] = max(dp[i][j], sum[j] - sum[i - 1] - r[i][j - 1]);
25 l[i][j] = min(dp[i][j], l[i + 1][j]);
26 r[i][j] = min(dp[i][j], r[i][j - 1]);
27 }
28 }
29 }
30 printf("%d\n", 2 * dp[1][n] - sum[n]);
31 }
32 }

时间: 2024-11-03 22:05:16

UVA 10891 Game of Sum(DP)的相关文章

uva 10635 Prince and Princess(DP)

uva 10635 Prince and Princess(DP) In an n x n chessboard, Prince and Princess plays a game. The squares in the chessboard are numbered 1, 2, 3 ... n*n, as shown below: Prince stands in square 1, make p jumps and finally reach square n*n. He enters a

HDU 1003:Max Sum(DP)

Max Sum Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 142742    Accepted Submission(s): 33225 Problem Description Given a sequence a[1],a[2],a[3]......a[n], your job is to calculate the max s

Uva 10891 Game of Sum(区间博弈dp)

10891 - Game of Sum Time limit: 3.000 seconds This is a two player game. Initially there are n integer numbers in an array and players A and B get chance to take them alternatively. Each player can take one or more numbers from the left or right end

UVA 10564-Paths through the Hourglass(DP)

题目大意:给出一个沙漏,包含一个倒三角和一个三角,每个方格有一个0到9的数字,从顶上某格走到底下某格,求得到一个特殊值的路径有多少条,并输出字典序最小的.路径用一个起点和一系列'L'和'R'的字符表示. 用a[i][j]表示第i行第j列的数字.由于要求字典序最小的,所以从下往上递推.用d[i][j][u]表示在(i,j)且和为u的路径有多少条,在上半部分是倒三角,可以由d[i+1][j][u-a[i][j]]和d[i+1][j+1][u-a[i][j]]相加递推而来,在下半部分是正三角,可以由d

UVA 10891 Game of Sum 区间dp

http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=19461 题目意思大致是给你一串数字,A,B两个人轮流从两端取一段数字并得到该串数字的和的点数,每个人都尽可能的多的点数,问A最多能比B多多少点. 区间dp,一开始打算分AB,但是发现太麻烦了,最后用dp(l,r)表示在区间l~r中先手能赢的的最多点数.假设A是区间(l,r)的先手的话,如果A选择了(l,k )// 或(k+1,r)的数字,那他的得分(l,r)的总分减去B在余

UVA 10891 Game of Sum(区间DP(记忆化搜索))

题目链接:https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=1832 题目大意: 两个人在玩一个游戏: 给你一行n个数字,每次只能从左端或者右端取一个或多个数字. 每个人的分值就是他们各自取得的数字之和. 假设两人都足够聪明,问先手最多能比后手多多少分. 解题思路: 其实题目意思就是先手最多能得到多少分. 设dp[l][r]是取完[l,r]的

UVA - 10891 Game of Sum (区间dp)

题意:AB两人分别拿一列n个数字,只能从左端或右端拿,不能同时从两端拿,可拿一个或多个,问在两人尽可能多拿的情况下,A最多比B多拿多少. 分析: 1.枚举先手拿的分界线,要么从左端拿,要么从右端拿,比较得最优解. 2.dp(i, j)---在区间(i, j)中A最多比B多拿多少. 3.tmp -= dfs(i + 1, r);//A拿了区间(l, i),B在剩下区间里尽可能拿最优 tmp是A拿的,dfs(i + 1, r)是B比A多拿的,假设dfs(i + 1, r)=y-x,y是B拿的,x是A

【UVA】108 - Maximum Sum(DP矩阵)

n^3的复杂度计算最小子矩阵,用了最大连续和的DP算法. 14273282 108 Maximum Sum Accepted C++ 0.013 2014-09-27  #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int INF = 1 << 30; const int maxn = 127 + 3; int mat[maxn][maxn]

uva 10313 Pay the Price (DP)

uva 10313 Pay the Price 题目大意:现在有300种面额的硬币(1~300),给出一个金额数,问这300种面额的硬币组成该金额数的方式有多少种.注意:该题的输入有三种模式,1个数n:n为金额数:2个数n, a:n为金额数,a为硬币个数上限:3个数n, a,b:n为金额数,a b为硬币个数的下限和上限. 解题思路:dp[i][j]表示面额i的金额,在硬币数不超过j的情况下,有几种组成方式.这个题目涉及到一个结论,用不超过i个硬币凑出面值j的方案种数,是和用面值不超过i的硬币凑出