一.题目链接
POJ-1179
二.题目大意:
给一个 n 边形,顶点为数值(整数),边为运算符(只包含 '+' 和 ‘*’ 两种)
现在让你删去其中的一条边
之后每次可以合并两个顶点,并且产生新的顶点,数值为原来两个顶点按照边上的运算符得到的结果
最终得到一个点
求点的最大值,并输出删去那条边可以得到最大值(若答案不唯一,则输出所有解)
三.分析:
思路与环形石子合并一样...
不过由于顶点的值为整数,可正可负,即最大值可能又两个最小值(负数)相乘而来,因此还要维护最小值
易得:
最大值 = max(最大值 + 最大值,最大值 * 最大值,最小值 * 最小值)
最小值 = min(最小值 + 最小值,最小值 * 最小值,最小值 * 最大值,最大值 * 最小值)
详见代码.
四.代码实现:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int M = (int)1e2;
const int inf = 0x3f3f3f3f;
int a[M + 5];
char b[M + 5];
int dp_max[M + 5][M + 5];
int dp_min[M + 5][M + 5];
/**
4
t -7 t 4 x 2 x 5
-7 + 4 * 2 * 5 + -7 + 4 * 2 * 5 +
**/
int main()
{
// freopen("input.txt", "r", stdin);
int n;
scanf("%d", &n);
getchar();
scanf("%c", &b[n]);
b[2 * n] = b[n];
for(int i = 2; i <= 2 * n; ++i)
{
if(i & 1)
{
getchar();
scanf("%c", &b[i / 2]);
b[n + i / 2] = b[i / 2];
}
else
{
scanf("%d", &a[i / 2]);
a[n + i / 2] = a[i / 2];
}
}
memset(dp_max, -inf, sizeof(dp_max));
memset(dp_min, inf, sizeof(dp_min));
for(int i = 1; i <= 2 * n; ++i)
{
dp_min[i][i] = a[i];
dp_max[i][i] = a[i];
}
for(int len = 2; len <= n; ++len)
{
for(int l = 1; l + len - 1 <= 2 * n; ++l)
{
int r = l + len - 1;
for(int k = l; k < r; ++k)
{
if(b[k] == 't')
{
dp_max[l][r] = max(dp_max[l][r], dp_max[l][k] + dp_max[k + 1][r]);
dp_min[l][r] = min(dp_min[l][r], dp_min[l][k] + dp_min[k + 1][r]);
}
else
{
dp_max[l][r] = max(dp_max[l][r], dp_max[l][k] * dp_max[k + 1][r]);
dp_max[l][r] = max(dp_max[l][r], dp_min[l][k] * dp_min[k + 1][r]);
dp_min[l][r] = min(dp_min[l][r], dp_min[l][k] * dp_min[k + 1][r]);
dp_min[l][r] = min(dp_min[l][r], dp_max[l][k] * dp_min[k + 1][r]);
dp_min[l][r] = min(dp_min[l][r], dp_min[l][k] * dp_max[k + 1][r]);
}
}
}
}
int ans = -inf;
for(int i = 1; i <= n; ++i)
ans = max(ans, dp_max[i][i + n - 1]);
printf("%d\n", ans);
for(int i = 1; i <= n; ++i)
{
if(dp_max[i][i + n - 1] == ans)
printf("%d ", i);
}
return 0;
}