POJ 1179 Polygon(环形DP 矩阵连乘)

题意:给出一个环 环上的每个点有一个值 点和点之间用一条边相连,边上有一个运算符,要求删除一条边,然后对剩下的线两两顶点合并运算.求通过运算能得到的最大值.并求出删除某条边后能经过运算得到这个最大值的这些边.

由于N<=50,所以可以枚举每一个断开点,对断开后形成的线做类似矩阵连乘的DP,这样的复杂度为O(N^4)还是可以接受的.

要注意的是最大值可能有最小值相乘得到(负数),所以也要记录最小值.

设dp1[i][j]为从i到j的最大运算结果,dp2为最小值

那么

dp1[i][j] = max(dp1[i][j], dp1[i][k] + dp1[k + 1][j] | i<=k<j)

dp1[i][j] = max(dp1[i][j], dp1[i][k] * dp1[k + 1][j], dp1[i][k] * dp2[k + 1][j], dp2[i][k] * dp1[k + 1][j], dp2[i][k] * dp2[k + 1][j] | i <= k < j)

dp2的方程和上面差不多,只不过max变成了min

#include <cstdio>
#include <memory.h>
#include <climits>
#include <algorithm>
using namespace std;
const int MAX = 122;

char edg[MAX][2];
int vet[MAX];
int dp1[MAX / 2][MAX / 2], dp2[MAX / 2][MAX / 2];
int final[MAX];
int N;

int main(int argc, char const *argv[]){
	int ans_max = INT_MIN, N;
	scanf("%d", &N);
	for(int i = 0; i < N; ++i){
		scanf("%s %d", edg[i], &vet[i]);
		edg[i + N][0] = edg[i][0];
		vet[i + N] = vet[i];
	}

	//enumerate every cutting point
	for(int k = 0; k < N; ++k){
		//base cases
		for(int i = 0; i < N; ++i){
			dp1[i][i] = vet[k + i];
			dp2[i][i] = vet[k + i]; 
		}
		//do DP for every line after cutting
		for(int i = 1; i < N; ++i){
			for(int j = 0; j + i< N; ++j){
				int maxv = INT_MIN, minv = INT_MAX;
				for(int p = j; p < j + i; ++p){
					if(edg[p + k + 1][0] == 't'){
						maxv = max(maxv, dp1[j][p] + dp1[p + 1][j + i]);
						minv = min(minv, dp2[j][p] + dp2[p + 1][j + i]);
					}else{
						//minimum times minimum might result in maximum
						maxv = max(maxv, dp1[j][p] * dp1[p + 1][j + i]);
						maxv = max(maxv, dp1[j][p] * dp2[p + 1][j + i]);
						maxv = max(maxv, dp2[j][p] * dp1[p + 1][j + i]);
						maxv = max(maxv, dp2[j][p] * dp2[p + 1][j + i]);
						//vice versa
						minv = min(minv, dp1[j][p] * dp1[p + 1][j + i]);
						minv = min(minv, dp1[j][p] * dp2[p + 1][j + i]);
						minv = min(minv, dp2[j][p] * dp1[p + 1][j + i]);
						minv = min(minv, dp2[j][p] * dp2[p + 1][j + i]);
					}
				}
				dp1[j][j + i] = maxv;
				dp2[j][j + i] = minv;
			}
		}
		ans_max = max(ans_max, dp1[0][N - 1]);
		final[k] = dp1[0][N - 1];
	}
	printf("%d\n", ans_max);
	for(int i = 0; i < N; ++i){
		if(final[i] == ans_max){
			printf("%d ", i + 1);
		}
	}
	printf("\n");
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值