您的位置:首页 > 其它

Light OJ 1031 区间dp

2016-08-22 21:47 190 查看
Description

You are playing a two player game. Initially there are n integer numbers in an array and player 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 Astarts the game then how much more point can player A get than player B?

Input

Input starts with an integer T (≤ 100), denoting the number of test cases.

Each case contains a blank line and an integer N (1 ≤ N ≤ 100) denoting the size of the array. The next line contains N space separated integers. You may assume that no number will contain more than 4 digits.

Output

For each test case, print the case number and the maximum differe
4000
nce that the first player obtained after playing this game optimally.

Sample Input

2

 

4

4 -10 -20 7

 

4

1 2 3 4

Sample Output

Case 1: 7

Case 2: 10

    题意:两人做游戏,一人可先手从左侧或者右侧选择连续区间的数字,另一人后手选择左侧或者右侧连续区间的数字,求最终先手游戏者比后手游戏者最终得到数字和的差的最大。

分析:区间dp解决。第一次接触到此类题目,理解dp方程就花了不少时间。dp[i][j]的意思指以在i为起点,j为终点的区间的得到的最大的差值。dp方程如下:

dp[j][i+j]=max(dp[j][i+j], sum[k]-sum[j-1]-dp[k+1][i+j]);

        dp[j][i+j]=max(dp[j][i+j], sum[i+j]-sum[k]-dp[j][k]);

    在此j为区间起点,i为以j为起点,i为长度的区间。k属于j到i+j的区间,第一个方程的意思指先手拿左侧j至k的区间,然后k+1至i+j的区间则沦为后手,dp[k+1][i+j]区间的值即为先手比后手损失的值,用先手取得子区间的和减去损失值即为该区间所求值。当取得区间值大而损失值小时即为最优解。理解:最优情况在此区间必然存在先手后手问题,符合上述方程假设条件,这样就将大的区间转化为小的区间问题。

    第二个方程指的是先拿右边区间,处理左侧区间问题。在此不赘述。

    其他问题代码中有注释,见AC代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int maxn=105;
int dp[maxn][maxn],a[maxn],sum[maxn];
int main()
{
int T,k=0;
scanf("%d",&T);
while(T--)
{
k++;
memset(dp,0,sizeof(dp));
sum[0]=0;
int n;
scanf("%d",&n);
for(int i=1; i<=n; i++)
{
scanf("%d",&a[i]);
dp[i][i]=a[i];//i到i的区间上  先手比后手最多 多  a[i]
sum[i]=sum[i-1]+a[i];
}
for (int i=1; i<=n; i++)//i为区间延伸长度
{
for (int j=1; j+i<=n; j++) //j为起点
{
//	i+j 为区间末尾
dp[j][j+i]=sum[i+j]-sum[j-1];//初始为区间和  先手一把全拿
for (int k=j; k<i+j; k++)//对子区间
{
dp[j][i+j]=max(dp[j][i+j], sum[k]-sum[j-1]-dp[k+1][i+j]);//先手选择j至k部分的和  k+1至i+j区间即变为后手  减去后一区间损失的值
dp[j][i+j]=max(dp[j][i+j], sum[i+j]-sum[k]-dp[j][k]);//先手选择后一区间  则损失前一区间作为后手的代价
}
}
}
int res=dp[1]
;//1至n区间上 先手比后手拿到最多的值
printf("Case %d: %d\n", k, res);
}
}
    刷题太少,见识太短浅,时不我待。

    特记下,以备后日回顾。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: