以前做过类似的题目,几乎就一样,所以很快就做出来了。
dp[i][j]代表对区间[i,j]先出手的最优解,那么枚举所有决策,区间dp一下就ok了。
因为先手取完后,就轮到后手取,后手取就相当于对取完后的状态的先手取。
相减就是答案。
后来又自己捣鼓了半天,把O(n^3)优化成了O(n^2)。
感觉在动态规划中,如果状态转移方程涉及循环遍历,那么就可以考虑维护最优值,然后降一维。维护的最优值必须是跟循环变量有关的量。
O(n^3)代码
#include<bits/stdc++.h>
#define maxn 110
using namespace std;
typedef long long ll;
ll n;
ll a[maxn];
ll dp[maxn][maxn];
ll sum[maxn];
int main()
{
while(scanf("%lld",&n)==1&&n)
{
for(ll i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
sum[i]=sum[i-1]+a[i];
dp[i][i]=a[i];
}
for(ll i=2;i<=n;i++)
for(ll j=1;j+i-1<=n;j++)
{
dp[j][j+i-1]=sum[j+i-1]-sum[j-1];
for(ll k=1;k<i;k++)
{
dp[j][j+i-1]=max(dp[j][j+i-1],sum[j+k-1]-sum[j-1]-dp[j+k][j+i-1]);
dp[j][j+i-1]=max(dp[j][j+i-1],sum[j+i-1]-sum[j+i-1-k]-dp[j][j+i-1-k]);
}
}
printf("%lld\n",dp[1][n]);
}
return 0;
}
O(n^2)代码
#include<bits/stdc++.h>
#define maxn 110
using namespace std;
typedef long long ll;
ll n;
ll a[maxn];
ll dp[maxn][maxn];
ll sum[maxn];
ll f[maxn][maxn],g[maxn][maxn];
int main()
{
while(scanf("%lld",&n)==1&&n)
{
for(ll i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
sum[i]=sum[i-1]+a[i];
dp[i][i]=f[i][i]=g[i][i]=a[i];
}
for(ll i=2;i<=n;i++)
for(ll j=1;j+i-1<=n;j++)
{
dp[j][j+i-1]=sum[j+i-1]-sum[j-1];
f[j][j+i-1]=max(a[j]+f[j+1][j+i-1],a[j]-dp[j+1][j+i-1]);
g[j][j+i-1]=max(a[j+i-1]+g[j][j+i-2],a[j+i-1]-dp[j][j+i-2]);
dp[j][j+i-1]=max(dp[j][j+i-1],f[j][j+i-1]);
dp[j][j+i-1]=max(dp[j][j+i-1],g[j][j+i-1]);
}
printf("%lld\n",dp[1][n]);
}
return 0;
}