Game of sum
题目大意:
给你一个串数字,每次只能选一边,然后取走一些数,你拿到的数字就是你的分数,求先手的人可以比后手的人最多多拿多少分
区间DP问题
难就难在两头都可以取,而且对面也不傻,采取的也是最优策略···
依旧按照DP的思想,大问题转化为小问题
首先要直接计算最大差多少分是非常难的
但是知道先手最大拿多少分,那么就可以计算出后手拿多少分(反正总分就这么多)
这样,由已知推未知,那么最大值就是上一步中最小值加上这一步的值
先手的人是a,后手的人是b
每次更新dp数组的时候,m表示的是把b的当成先手的,选最大值,然而a不会让他拿到最大值的,只会让他拿到最小情况中的最大值
整个过程需要倒着来看,因为DP就是由上个状态递推下个状态,那么对于选数,整个过程都是反着
总的来说,每次更新dp就是看成a的先手机会先存着,让b先先手,那么为了让b虽然先手,但是拿到最小的
那么就要找其中先手的最小值,这样,a的先手机会就用来把那些剩下的数全选了,这样保证了b拿到了最小值
依次递推
然后更新dp数组,最后得出结果
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
int a[110],sum[110];
int dp[110][110];//dp[i][j]表示从i到j先手可以拿到的最大值
int main()
{
int n;
while(~scanf("%d",&n)&&n)
{
memset(sum,0,sizeof(sum));
for(int i=1; i<=n; i++)
{
scanf("%d",&a[i]);
sum[i]=sum[i-1]+a[i];
}
for(int i=1; i<=n; i++)//先初始化
{
dp[i][i]=a[i];
}
for(int l=2; l<=n; l++)
{
for(int i=1,j=l; j<=n; i++,j++)
{
int m,ans=-9999999;
for(int k=i; k<j; k++)
{
m=min(dp[i][k],dp[k+1][j]);//找出中间的最小值(m是后手拿到的分数)
m=sum[j]-sum[i-1]-m;//m表示上一笔选的值
if(m>ans)
{
ans=m;
}
}
if(sum[j]-sum[i-1]>ans)//如果一下选完可以最大值最大
{
ans=sum[j]-sum[i-1];
}
dp[i][j]=ans;
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
printf("%d ",dp[i][j]);
}
printf("\n");
}
}
}