题目大意:
俩小孩玩游戏, 轮流从左边或者右边选择连续若干个数( 除非全选, 否则只能选一边 ), 它们的和就是这个小孩这一次的得分, 然后删去选择的数, 问先手的小孩最多可以比后手的多多少分.
很明显的dp题是不是?
那么怎么设状态呢? dp[ i ][ j ]表示在[ l , r ]这一段区间内, 先手可以得到的最大分数. 那么怎么转移呢? 想来想去也没有什么想法.
这时候注意到一个地方: 每次只能选左边或者右边的若干个数, 那么假设现在剩下的是[ l , r ]这一段的数, 先手选择了[ l , k ], 剩下的就是[ k+1 , r ]; 先手选择[ k , r ], 剩下的就是[ l , k-1 ].
而剩下的又是一个类似的子问题了. 所以必定是从dp[ l ][ k ]和dp[ k ][ r ]转移( 其中k∈[ l , r ] ).
考虑到状态是先手的最大分数, 而这一轮先手选择之后, 后手变先手, 那么原后手也是按照最优策略选择, 所以也是枚举了子区间转移.
由于存在先后手转换, 而且二者都是按照最优策略, 所以当前先手肯定枚举k, 在两种方向( 选右边或者选左边 )中选择子区间最优策略下自己剩的分最多的方案.
而给自己能够剩的分是sum[ l ][ k ]-dp[ l ][ k ]或者sum[ k ][ j ]-dp[ k ][ j ].
这样转移就很明显了, 记得当前选择之后也有选择的分, 也要加入转移取max:
dp[ i ][ j ]=max( sum[ l ][ k ]-dp[ l ][ k ]+sum[ k+1 ][ r ] , sum[ k ][ r ]-dp[ k ][ r ]+sum[ l ][ k-1 ] ).
化简一下就是:
dp[ i ][ j ]=max( -dp[ l ][ k ] , -dp[ k ][ r ] )+sum[ l ][ r ].
sum用前缀和搞一下, 枚举一下k, 注意初状态为0和多组数据, 剩下的就是没有什么技术含量的码代码了.
//made by Crazy01
#include<queue>
#include<math.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
#define inf 1<<30
#define ll long long
#define db double
#define c233 cout<<"233"<<endl
#define mem(s) memset(s,0,sizeof(s))
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
const int N=105;
using namespace std;
int dp[N][N],sum[N],a[N];
int n;
inline int gi(){
int x=0,res=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')res*=-1;ch=getchar();}
while(ch<='9'&&ch>='0')x=(x<<1)+(x<<3)+ch-48,ch=getchar();
return x*res;
}
void init(){
n=gi(); if(!n)exit(0);
for(int i=1;i<=n;i++)
a[i]=gi(),sum[i]=sum[i-1]+a[i];
}
void work(){
for(int det=1;det<=n;det++)
for(int i=1;i+det-1<=n;i++){
if(det==1){dp[i][i]=a[i]; continue;}
int j=i+det-1; dp[i][j]=-inf;
for(int k=i;k<j;k++)
dp[i][j]=max(dp[i][j],-dp[i][k]);
for(int k=j;k>i;k--)
dp[i][j]=max(dp[i][j],-dp[k][j]);
dp[i][j]=max(dp[i][j],0);
dp[i][j]+=sum[j]-sum[i-1];
}
printf("%d\n",dp[1][n]-sum[n]+dp[1][n]);
}
int main(){
while(1){
init();
work();
}
return 0;
}
顺带说一句, 这一题是我人生压行的巅峰, 上面那个代码被我压成了这个鬼样子( 除开made by Crazy01只有323B, 只是为了争夺vjudge上这一题的rank 1....):
//made by Crazy01
#include<iostream>
#define P f[i][j]
using namespace std;int f[105][105],s[105],n,i,w,k,j;int main(){cin>>n;while(n){for(i=1;i<=n;i++)cin>>s[i],s[i]+=s[i-1];for(w=0;w<n;w++)for(i=1;i+w<=n;i++){j=i+w;P=0;for(k=i;k<j;k++)P=max(P,-f[i][k]);for(k=j;k>i;k--)P=max(P,-f[k][j]);P+=s[j]-s[i-1];}cout<<f[1][n]*2-s[n]<<endl;cin>>n;}}