题意:多边形游戏,有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"); } }