codevs3196 黄金宝藏

题目描述 Description

小毛终于到达宝藏点,他意外地发现有一个外星人(名叫Pluto)。宝藏是一些太空黄金,有n堆排成一行,每堆中有xi颗黄金。小毛和Pluto决定轮流从中取出黄金,规则是每次只能从最左边或最右边取出一堆黄金,直到所有黄金被取出。小毛先取,两人都以最优策略进行选取,求两人的最后所得。

输入描述 Input Description

第一行是正数n(≤500);第二行为n个正整数xi(≤300),表示每堆黄金的个数。

输出描述 Output Description

仅两个整数,分别表示小毛和Pluto的得分,以空格隔开。

样例输入 Sample Input

4 7 2 9 5 2

样例输出 Sample Output

18 11

正解:DP

解题报告:
  看到这是博弈的时候有一点慌。。。好吧其实只用了思想。

  显然DP可行,f[i][j]表示从i到j的区间的最优策略,最后f[1][n]即先手得分,总和减掉这个最优值即为后手得分。

  转移也比较好想,只是有一个地方比较神。显然由从左端选了一堆或者从右端选了一堆转移过来,而我们取得是较小的那个值,因为我们需要后手决策尽可能贡献小。

  细节看代码吧。

 1 //It is made by jump~
 2 #include <iostream>
 3 #include <cstdlib>
 4 #include <cstring>
 5 #include <cstdio>
 6 #include <cmath>
 7 #include <algorithm>
 8 #include <ctime>
 9 #include <vector>
10 #include <queue>
11 #include <map>
12 #include <set>
13 #ifdef WIN32   
14 #define OT "%I64d"
15 #else
16 #define OT "%lld"
17 #endif
18 using namespace std;
19 typedef long long LL;
20 const int MAXN = 501;
21 int n;
22 int a[MAXN],sum[MAXN];
23 int f[MAXN][MAXN];//f[i][j]表示从i到j的最优策略得分
24 
25 inline int getint()
26 {
27        int w=0,q=0;
28        char c=getchar();
29        while((c<'0' || c>'9') && c!='-') c=getchar();
30        if (c=='-')  q=1, c=getchar();
31        while (c>='0' && c<='9') w=w*10+c-'0', c=getchar();
32        return q ? -w : w;
33 }
34 
35 inline void work(){
36     n=getint();
37     for(int i=1;i<=n;i++) a[i]=getint(),f[i][i]=a[i],sum[i]=sum[i-1]+a[i];
38     for(int len=1;len<=n-1;len++)
39     for(int i=1;i<=n-len;i++)
40         f[i][i+len]=sum[i+len]-sum[i-1]-min(f[i+1][i+len],f[i][i+len-1]);//减去部分是后手的决策最小值
41     printf("%d %d",f[1][n],sum[n]-f[1][n]);//总和减去先手最优决策即为后手得分
42 }
43 
44 int main()
45 {
46   work();
47   return 0;
48 }
原文地址:https://www.cnblogs.com/ljh2000-jump/p/5656324.html