Light OJ 1031 Easy Game 区间DP

题目描述:

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 A starts 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 difference 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

题目分析:

一个长度为n的数列,A与B在进行取数游戏,他们只能从数列的一端(左端或右端)取,一次可以取若干个。若现在A先取,求A最多能赢B多少?
区间DP,我是用递归的方法,将大区间分解为数个小区间,每一次该小区间的值更新,同时进行先后手转化。因为每一次选取都是选取该区间内最大的值,每一次都是从左向右,或从右向左。DP题的思想还是需要自己慢慢琢磨。

代码如下:

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

const int INF =0x3f3f3f3f;
using namespace std;


int num[110];
int sum[110][110];//sum[i][j]表示从i到j的和
int dp[110][110];//dp[i][j]表示从i到j,A能赢得最多
int T;



int DP(int s,int e)//递归求法将1~n的区间分解为数个小区间
{
    if (dp[s][e]!=-1) return dp[s][e];//dp终点1,已经确定的最大值
    if (s>e) return dp[s][e]=0;//dp终点2,越界,最大值为0
    int mm=-INF;
    for(int i=s; i<=e; i++)//在每一个区间s~e之间,遍历区间内所有点
    {
        mm=max(mm,sum[s][i]-DP(i+1,e));
        //相当于区间s~i为A取,i+1~e之间的最大值为B所取,这里相当于完成了先后手的交换
        mm=max(mm,sum[i][e]-DP(s,i-1));
        //相当于区间i~e为A取,s~i-1之间的最大值为B所取,这里相当于完成了先后手的交换
    }
    //printf("%d %d ==%d\n",s,e,mm);//中间值
    return dp[s][e]=mm;//s~e之间的最优解
}

int main()
{
    scanf("%d",&T);
    for(int t=1; t<=T; t++)
    {
        int n;
        scanf("%d",&n);
        for(int i=1; i<=n; i++)
           scanf("%d",&num[i]);
        memset(sum,0,sizeof(sum));
        for(int i=1; i<=n; i++)
        {
            int tmp=0;
            for(int j=i; j<=n; j++)
            {
                tmp+=num[j];
                sum[i][j]=tmp;
            }
        }
        memset(dp,-1,sizeof(dp));
        for(int i=1; i<=n; i++) dp[i][i]=num[i];//区间i~i的值就是那个位置的值,相当于先手取了这个值
        DP(1,n);
        printf("Case %d: %d\n",t,dp[1][n]);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值