lightoj 1031 - Easy Game 【區間dp】360 2017筆試程式設計題3

MissZhou要努力發表於2016-04-29

Description

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

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 containsN space separated integers. You may assume that no number will contain more than4 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


題意:兩個小孩輪流從一段數字的左邊或者右邊取走連續的一段,問先手比後手最多多得多少分

尼瑪,區間dp居然還有這麼玩的==我們可以想到這種題都是小區間合併成大區間的,然而這種類似於博弈的做法實在是讓人頭疼QAQ,其實倒也不難,既然第一個人的值是有第二個人推導過來的,那麼用的值也是第二個人得到的最優值,這麼來說,先手後手只是相對概念了==

把[i,j]分成[i,k] [k+1,j]兩段,如果取走左邊的就是左邊的和減去右邊那部分後手比先手多的部分,反之亦然


#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int dp[109][109],sum[109];
int t,n,cas;
int main()
{
 //   freopen("cin.txt","r",stdin);
    scanf("%d",&t);
    cas=1;
    while(t--)
    {
        scanf("%d",&n);
        memset(dp,0,sizeof(0));
        for(int i=1;i<=n;i++)scanf("%d",&dp[i][i]);
        sum[1]=dp[1][1];
        for(int i=2;i<=n;i++) sum[i]=sum[i-1]+dp[i][i];
        for(int len=1;len<=n;len++)
            for(int l=1;l+len<=n;l++)
            {
                int r=l+len;
                dp[l][r]=sum[r]-sum[l-1];
                for(int k=l;k<r;k++)
                dp[l][r]=max(dp[l][r],max(sum[k]-sum[l-1]-dp[k+1][r],sum[r]-sum[k]-dp[l][k]));
            }
        printf("Case %d: %d\n",cas++,dp[1][n]);
    }
    return 0;
}


翻部落格就是覺得這個題眼熟麼,360 2017春招筆試:

分金子(奇虎360 2017春招真題)

題目描述

A、B兩夥馬賊意外地在一片沙漠中發現了一處金礦,雙方都想獨佔金礦,但各自的實力都不足以吞下對方,經過談判後,雙方同意用一個公平的方式來處理這片金礦。處理的規則如下:他們把整個金礦分成n段,由A、B開始輪流從最左端或最右端佔據一段,直到分完為止。 

馬賊A想提前知道他們能分到多少金子,因此請你幫忙計算他們最後各自擁有多少金子?(兩夥馬賊均會採取對己方有利的策略)

輸入

測試資料包含多組輸入資料。輸入資料的第一行為一個正整數T(T<=20),表示測試資料的組數。然後是T組測試資料,每組測試資料的第一行包含一個整數n,下一行包含n個數(n <= 500 ),表示每段金礦的含金量,保證其數值大小不超過1000。


 
樣例輸入

6

4 7 2 9 5 2

10

140 649 340 982 105 86 56 610 340 879


輸出

對於每一組測試資料,輸出一行"Case #id: sc1 sc2",表示第id組資料時馬賊A分到金子數量為sc1,馬賊B分到金子數量為sc2。詳見樣例。


樣例輸出

Case #1: 18 11

Case #2: 3206 981


一樣的 就是k只能取l+1 或者 r-1

     #include <iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    int dp[509][509],sum[509];
    int t,n,cas;
    int main()
    {
        freopen("cin.txt","r",stdin);
        scanf("%d",&t);
        cas=1;
        while(t--)
        {
            scanf("%d",&n);
            memset(dp,0,sizeof(0));
            for(int i=1;i<=n;i++)scanf("%d",&dp[i][i]);
            sum[1]=dp[1][1];
            for(int i=2;i<=n;i++) sum[i]=sum[i-1]+dp[i][i];
            for(int len=1;len<=n;len++)
                for(int l=1;l+len<=n;l++)
                {
                    int r=l+len;
                    dp[l][r]=max(sum[l]-sum[l-1]-dp[l+1][r],sum[r]-sum[r-1]-dp[l][r-1]);
                }
            printf("Case #%d: %d %d\n",cas++,(sum[n]+dp[1][n])/2,(sum[n]-dp[1][n])/2);
        }
        return 0;
    }



相關文章