题意:多边形游戏,有N个顶点的多边形,3 <= N <= 50 ,多边形有N条边,每个顶点中有一个数字(可正可负),每条边上或者是“+”号,或者是“*”号。边从1到N编号,首先选择一条边移去,然后进行如下操作:1 选择一条边E和边E连接着的两个顶点V1,V2;2 用一个新的顶点代替边E和V1、V2,新顶点的值为V1、V2中的值进行边上代表的操作得来(相加或相乘)。当最后只剩一个顶点,没有边时,游戏结束。现在的任务是编程求出最后的顶点能获得的最大值,以及输出取该最大值时,第一步需移去的边,如果有多条符合条件的边,按编号从小到大输出。
思路:发现可以用矩阵链乘来做,循环也比较好处理,将多边形按顺序复制一次即可,求链乘最大长度为n即可。但是由于定点的数据可能是负数,所以最大值可能出现负负得正的情况,所以还需要保存矩阵链的最小值。
#include <stdio.h>
#include <string.h>
#define INF 0x3fffffff
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
#define N 150
int n,op[N],dp[N][N][2],ans[N];//dp第三维为0表示最小值,最大表示最大值
int dd(int x){
return (x<<1)-1;
}
int computemax(int i,int id,int j){
if(op[id])
return max(dp[i][id-1][1] * dp[id][j][1], dp[i][id-1][0]*dp[id][j][0]);
return dp[i][id-1][1] + dp[id][j][1];
}
int computemin(int i,int id,int j){
if(op[id])
return min(min(dp[i][id-1][0]*dp[id][j][1],dp[i][id-1][1]*dp[id][j][0]),dp[i][id-1][0]*dp[id][j][0]);
return dp[i][id-1][0] + dp[id][j][0];
}
int main(){
while (scanf("%d\n",&n)!=EOF) {
char ch;
int i,j,p,k,res = -INF,num=0;
memset(dp,0,sizeof(dp));
for(i = 1;i<=n;i++){
scanf("%c %d ",&ch,&k);
dp[i][i][0] = dp[i][i][1] = dp[i+n][i+n][0] = dp[i+n][i+n][1] = k;
if(ch == 't')
op[i] = op[i+n] = 0;
else if(ch == 'x')
op[i] = op[i+n] = 1;
}
for(k = 1;k<n;k++)
for(i = 1;i<=dd(n)-k;i++){
j = i+k;
dp[i][j][0] = INF;
dp[i][j][1] = -INF;
for(p = i+1;p<=j;p++)
dp[i][j][0] = min(dp[i][j][0],computemin(i, p, j)),
dp[i][j][1] = max(dp[i][j][1],computemax(i, p, j));
}
for(i = 1;i<=n;i++){
if(dp[i][i+n-1][1] > res){
res = dp[i][i+n-1][1];
num = 0;
ans[num++] = i;
}else if(dp[i][i+n-1][1] == res)
ans[num++] = i;
}
printf("%d\n",res);
for(i = 0;i<num;i++)
printf("%d ",ans[i]);
printf("\n");
}
}