HiHo第173周 A Game 区间DP

来源:互联网 发布:淘宝号如何实名认证 编辑:程序博客网 时间:2024/05/29 16:58
时间限制:10000ms
单点时限:1000ms
内存限制:256MB

描述

Little Hi and Little Ho are playing a game. There is an integer array in front of them. They take turns (Little Ho goes first) to select a number from either the beginning or the end of the array. The number will be added to the selecter's score and then be removed from the array.

Given the array what is the maximum score Little Ho can get? Note that Little Hi is smart and he always uses the optimal strategy.

输入

The first line contains an integer N denoting the length of the array. (1 ≤N ≤ 1000)

The second line contains N integers A1, A2, ... AN, denoting the array. (-1000 ≤Ai ≤ 1000)

输出

Output the maximum score Little Ho can get.

样例输入
4-1 0 100 2
样例输出
99

题意:有一个数组,小hi 和小ho要从中选一些数(小ho先选),规定只能选一头或一尾,问小ho能选的数的最大和。

题目分析:

1.枚举左右端点

每次轮到小Hi或者小Ho时,他们都面临同样的二选一决策:是拿走最左边的数,还是拿走最右边的数。

由于每次只能从数组首/尾拿走一个数,所以小Hi和小Ho在任何时候面对的残局都只可能是一段连续的子数组A[i..j]。我们不妨用dp[i][j]表示当面对A[i..j],先手最多能获得的得分。

如果我们能计算出所有f[i][j]的值,那么显然f[1][n]就是最终答案。

对于i < j的情况,先手P需要决策是拿走A[i]还是拿走A[j]?

如果拿走A[i],那么对手Q面对的是A[i+1 .. j],Q最多能获得的得分是dp[i+1][j]。而且Q一定会按照得到dp[i+1][j]这个得分的方式进行决策。所以先手P最大得分是sum(A[i .. j]) - f[i+1][j]。(A[i][j]的分数和减去P的得分)

同理如果拿走A[j],先手P最大得分会是sum(A[i .. j]) - f[i][j-1]。

由于此时先手P可以二选一,所以dp[i][j] = max{ sum(A[i .. j]) - dp[i+1][j], sum(A[i .. j]) - dp[i][j-1] }= sum(A[i .. j]) - min(dp[i+1][j], f[i][j-1])。

代码一种为递归一种为迭代

迭代:

#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>#include <vector>#include <map>#include <stack>using namespace std;const int maxn = 1010;int dp[maxn][maxn];int arr[maxn];int sum[maxn];int n;int main(){    while(scanf("%d", &n) != EOF)    {        memset(dp, 0, sizeof(dp));        memset(sum, 0, sizeof(sum));        for(int i = 1; i <= n; i++) {            scanf("%d", &arr[i]);            sum[i]=sum[i-1]+arr[i];            dp[i][i] = arr[i];        }        for(int i = n; i >= 1; i--)  {   //从后往前最后才到达dp[1][n];            for(int j = i; j <= n; j++)  {                dp[i][j]=max(sum[j]-sum[i-1]-dp[i][j-1], sum[j]-sum[i-1]-dp[i+1][j]);            }        }        printf("%d\n", dp[1][n]);    }    return 0;}

递归:

#include<bits/stdc++.h>  using namespace std;  int a[2000],d[1001][1001];  int v[1001][1001];//标记是否访问过  int dfs(int l,int r)  {      if(l>r)return 0;      if(v[l][r])return d[l][r];//已经知道d[l][r]      v[l][r]=1;      d[l][r]=max(a[l]+min(dfs(l+2,r),dfs(l+1,r-1)),a[r]+min(dfs(l+1,r-1),dfs(l,r-2)));//因为小Hi不会让小Ho取得较大分数,所以中间用min      return d[l][r];  }  int main()  {      int n;      scanf("%d",&n);      memset(v,0,sizeof v);      for(int i=1;i<=n;i++)scanf("%d",&a[i]);      cout<<dfs(1,n)<<endl;  } 

2.枚举长度和起点

    #include<bits/stdc++.h>      using namespace std;        const int maxn=1100;         int n,m,dp[maxn][maxn],a[maxn],sum[maxn];         int main()      {          int i,j,k,t;          scanf("%d",&n);          for(i=1;i<=n;i++)          scanf("%d",&a[i]),dp[i][1]=a[i],sum[i]=sum[i-1]+a[i];          for(i=2;i<=n;i++)    //长度        {              for(j=1;j+i-1<=n;j++)    //起点            {                  dp[j][i]=max(sum[j+i-1]-sum[j-1]-dp[j][i-1],sum[j+i-1]-sum[j-1]-dp[j+1][i-1]);              }          }          printf("%d\n",dp[1][n]);          return 0;      }  




原创粉丝点击