再是一题动归
传送门(洛谷)
题目描述:
输入数据
6
4 7 2 9 5 2
输出数据
18 11
此题是一区间动归题,根据题意,由于两个人都是最优策略,以f[i][j]表示一号选手的i到j号数字的最优值,二号则直接用总值减去即可。
再用sum[i]表示1到i号数字的和,一个递推式:
sun[i]=sum[i-1]+a[i]
再者,我们每次求f[i][j]时,可以从两边拿数,则有状态转移方程:
f[i][j]=max((sum[j]-sum[i-1])-f[i+1][j],(sum[j]-sum[i-1])-f[i][j-1]);
(sum[j]-sum[i-1])-f[i+1][j]解释:
我们假设我们是先手,表示我们取了a[i],然后我们就变成了后手。而我们的对手成了先手。
我们的对手是很聪明的,所以他会取最优的。
那在我们这次操作之后直到游戏结束,他取得的数的和就一定是f[i+1][j]
剩下来的数的和就是我们取到的值s[i+1][j]-f[i+1][j]
再加上我们之前取的数a[i]就是s[i][j]-f[i+1][j]
而我们是假设自己是先手,所以正真的玩家一就是我们取得值,也就是s[i][j]-f[i+1][j]
当我们取a[j]时同理。
Code
/***********************
* Problem:P2734 游戏 A Game *
* User: mzg1824_TY *
* Language: C++ *
* Date:2019.5.3 *
* Algorithm:区间动态规划 *
**********************/
#include<bits/stdc++.h>
#define rep(i,a,b) for(register int i=(a);i<=(b);i++)
#define don(i,a,b) for(register int i=(a);i>=(b);i--)
using namespace std;
const int maxn=1e5+10;
const int maxm=1e3+10;
int n;
int a[maxn],sum[maxn];
int f[maxm][maxm];
template <class t> inline void read(t &x)
{
x=0;char ch=getchar();int f=1;
while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){x=10*x+ch-'0';ch=getchar();}
x*=f;
}
template <class t> inline void write(t x)
{
if(x<0) {putchar('-');x=~x+1;}
if(x>9) write(x/10);
putchar(x%10+48);
}
/*-----------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------*/
void readdata()
{
memset(f,false,sizeof(f));
read(n);
rep(i,1,n) {
read(a[i]);
sum[i]=sum[i-1]+a[i];//记录前缀和
f[i][i]=a[i];//初始化
}
}
void work()
{
int tot=0;
don(i,n-1,1)
rep(j,i+1,n) {
f[i][j]=max((sum[j]-sum[i-1])-f[i+1][j],(sum[j]-sum[i-1])-f[i][j-1]);
//对于此我们有两种拿取方法,参照上面
}
rep(i,1,n) tot+=a[i];
write(f[1][n]);
printf(" ");
write(tot-f[1][n]);
}
int main()
{
freopen("input.txt","r",stdin);
readdata();
work();
return 0;
}