http://poj.org/problem?id=1179
题意:给定一个环, 求在其中断开一条边之后,最终所能得到的分数最高的所有方法。
思路:枚举+dp。给定的N的范围为1-50。因为给定的是一个环,我们只要段开其中的任意一条边,环就变成了一条线,这样我们只要枚举断开的那条边,然后求出给定的线的最高得分就可以解决该问题,在求有N个点的线的最高得分的时候,我们可以用dp[i][j]表示将i到j段消除所能得到的最高分,状态转移方程为:dp[i][j] = max{ dp[i][k-1] ope dp[k][j] , for i<k<=j} 其中的ope视+或*而定。 但是这里还有一个问题,当运算符是*的时候,dp[i][k-1]和dp[k][j]并不一定去最大的时候才有dp[i][j]取最大,也就是说这时候不满足子问题最优化的原则,导致这个问题的原因是当两个负数相乘的时候也可以得到一个比较大的正数, 因此我们这里还需要知道[i,j]内的最小值,这样才能最终得出最大值,最终的状态转移方程为:
当运算符为‘+’时:
dp[i][j][1] = MAX( dp[i][k-1][1] +dp[k][j][1]);
dp[i][j][0] = MIN( dp[i][k-1][0] + dp[k][j][0]);
当运算符为'*'时:
dp[i][j][0] = MIN (dp[i][k][0] * dp[k+1][j][0], dp[i][k][0] * dp[k + 1][j][1], dp[i][k][10] * dp[k + 1][j][0], dp[i][k][1] * dp[k + 1][j][1]);
dp[i][j][1] = MAX (dp[i][k][0] * dp[k+1][j][0], dp[i][k][0] * dp[k + 1][j][1], dp[i][k][10] * dp[k + 1][j][0], dp[i][k][1] * dp[k + 1][j][1]);
时间复杂度为O(N^4) ;
另外由于本题的最值可以取任意的值(包括负数),故本意最好不要用记忆化搜索。代码:
#include<stdio.h>
#include<string.h>
#define MAX(a,b) (a)>(b)?(a):(b)
#define MIN(a,b) (a)<(b)?(a):(b)
const long long INF = 999999999999999ll ;
long long N ;
long long val[110] ;
long long ope[110] ;
long long dp[110][110][2] ;
long long res[55],ren ;
void DP(int a, int b){
for(int j=a;j<=b;j++){
dp[j][j][0] = dp[j][j][1] = val[j] ;
}
for(int len =2 ;len<=N;len++){
for(int i=a;i+len-1<=b;i++){
int j = i + len -1 ;
dp[i][j][1] = - INF ;
dp[i][j][0] = INF ;
for(int k=i+1;k<=j;k++){
int aa = ope[k] ;
if(aa == 0){
dp[i][j][0] = MIN(dp[i][j][0] , dp[i][k-1][0] + dp[k][j][0]);
dp[i][j][1] = MAX(dp[i][j][1] , dp[i][k-1][1] + dp[k][j][1]);
}
else{
dp[i][j][0] = MIN(dp[i][j][0] , dp[i][k-1][0] * dp[k][j][0]);
dp[i][j][0] = MIN(dp[i][j][0] , dp[i][k-1][1] * dp[k][j][0]);
dp[i][j][0] = MIN(dp[i][j][0] , dp[i][k-1][0] * dp[k][j][1]);
dp[i][j][0] = MIN(dp[i][j][0] , dp[i][k-1][1] * dp[k][j][1]);
dp[i][j][1] = MAX(dp[i][j][1] , dp[i][k-1][1] * dp[k][j][1]);
dp[i][j][1] = MAX(dp[i][j][1] , dp[i][k-1][0] * dp[k][j][1]);
dp[i][j][1] = MAX(dp[i][j][1] , dp[i][k-1][1] * dp[k][j][0]);
dp[i][j][1] = MAX(dp[i][j][1] , dp[i][k-1][0] * dp[k][j][0]);
}
}
}
}
}
int main(){
char ch[5] ;
while(scanf("%lld",&N) == 1){
for(int i=1;i<=N;i++){
scanf("%s%lld",ch,&val[i]);
if(ch[0]=='t'){
ope[i] = 0 ;
}
else{
ope[i] = 1 ;
}
}
for(int i=N+1;i<=2*N;i++){
val[i] = val[i-N] ;
ope[i] = ope[i-N] ;
}
long long _max = -INF ;
ren = 0 ;
for(int i=1;i<=N;i++){
int s = i ,e = i + N -1 ;
DP(s,e);
//printf("%d %lld\n",i,dp[s][e][1]);
if(_max < dp[s][e][1]){
_max = dp[s][e][1] ;
ren = 0 ;
res[++ren] = i ;
}
else if(_max == dp[s][e][1]){
res[++ren] = i ;
}
}
printf("%lld\n",_max);
for(int i=1;i<ren;i++){
printf("%d ",res[i]);
}
printf("%d\n",res[ren]);
}
return 0 ;
}