1. 程式人生 > >取數遊戲(博弈+動規)

取數遊戲(博弈+動規)

題目描述

有如下一個雙人遊戲:N個正整數的序列放在一個遊戲平臺上,兩人輪流從序列的兩端取數,每次有數字被一個玩家取走後,這個數字被從序列中去掉並累加到取走該數的玩家的得分中,當數取盡時,遊戲結束。以最終得分多者為勝。

編一個執行最優策略的程式,最優策略就是使自己能得到在當前情況下最大的可能的總分的策略。你的程式要始終為兩位玩家執行最優策略。

輸入第1行包括一個正整數N(2≤N≤100), 表示序列中正整數的個數。輸入第2行包含用空格分隔的N個正整數(1≤所有正整數≤200)。

只有一行,用空格分隔的兩個整數: 依次為先取數玩家和後取數玩家的最終得分。

樣例輸入複製

6 
4 7 2 9 5 2

樣例輸出複製

18 11

sum[i][j]表示從i位到j位所有的和,dp[i][j]表示從i位到j位使自身取到的所有值的最大和

想一下,先手要取掉兩端中的一個值,此時無論先取者還是後取者都希望自己取的是最優的。

何為最優?

都保證自己會取得保證取完後總和相對大的,如何表示?

對於先手而言,

先取左邊還是右邊?當先手取完,就輪到後手去,後手一定會選擇當前能令他得到最大分數的策略,其實當先手在[x, y]區間兩端取走一個數,那麼後手面臨兩個狀態[x+1, y]和[x, y-1],先手想要取得最大值,一定會想讓後手取這兩種狀態中的較小值,

即:dp[i][j]=sum[i][j]-min(dp[i+1][j],dp[i][j-1]),後半部分為後者所得和,前者所得和為總和減後者的和。。。。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int a[105],sum[105][105],dp[105][105];
int main(){
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        scanf("%d",&a[i]);
        dp[i][i]=a[i];
        sum[i][i]=a[i];
    }
    for(int i=0;i<n;i++){
        for(int j=i+1;j<n;j++){
            sum[i][j]=sum[i][j-1]+a[j];
        }
    }
    for(int i=n-1;i>=0;i--){
        for(int j=i;j<n;j++){
            dp[i][j]=sum[i][j]-min(dp[i+1][j],dp[i][j-1]);
        }
    }
    printf("%d %d\n",dp[0][n-1],sum[0][n-1]-dp[0][n-1]);
}