多边形题解
题目
法1(直接按照题意实现)
分析
这种方法不是很难…吧。用一个循环枚举从第i个点切开,然后就是一个石子合并 搭配我的bolg食用更佳
注意
这道题由于有乘号,而负数 * 负数 = 整数,所以要保存一个最小值,还要保存一个最大值
代码高亮
可能是我写麻烦了,代码非常长
#include <queue>
#include <cstdio>
using namespace std;
const int MAXN = 55;
const int INF = 0x3f3f3f3f;
int n, _max = -INF;
int a[MAXN], tema[MAXN];
bool s[MAXN], tems[MAXN];
//s[i]和tems[i] = 1表示这一位为加号(i与i - 1的连接的符号)
//tema, tems存的是"切开"后的链
int f[MAXN][MAXN][2];
//f[i][j][0]存储的是i~j(tem数组)的最大值
//f[i][j][1]存储的是i~j(tem数组)的最小值
void MakeSet(int);//将"切开"后的链给到tema 和 tems中
int work();//石子合并求最大值
int Min(int x, int y) {return x < y ? x : y;}
int Max(int x, int y) {return x > y ? x : y;}
queue<int> ans;
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
char tem[3];
scanf("%s", tem + 1);
if(tem[1] == 't') {//加号
s[i] = 1;
}
else {//乘号
s[i] = 0;
}
scanf("%d", &a[i]);
}
for(int i = 1; i <= n; i++) {//从i处为开头(从i处切开)
MakeSet(i);//初始化
int tem = work();//算出最大值
if(tem == _max)//与最大值相同
ans.push(i);//多一种方法
if(tem > _max) {//大于最大值
while(!ans.empty()) ans.pop();//清空
ans.push(i);//记录下这种方法
_max = tem;//更新最大值
}
}
printf("%d\n", _max);
while(!ans.empty()) printf("%d ", ans.front()), ans.pop();
return 0;
}
void MakeSet(int x) {//从x处切开
int sum = 0;
for(int i = x; i <= n; i++) {
tema[++sum] = a[i];
tems[sum] = s[i];
}
for(int i = 1; i <= x - 1; i++) {
tema[++sum] = a[i];
tems[sum] = s[i];
}
}
int work() {//石子合并
for(int i = 1; i <= n; i++) f[i][i][0] = tema[i], f[i][i][1] = tema[i];//初始化
for(int i = 1; i <= n; i++) {//枚举长度
for(int j = 1; j <= n - i; j++) {//枚举左端点
int l = j, r = i + j;
f[l][r][0] = -INF;//求最大值
f[l][r][1] = INF;//求最小值
for(int k = l; k <= r - 1; k++) {//枚举断点(和后面一个合并)
if(tems[k + 1] == 1) {//加号
f[l][r][0] = Max(f[l][r][0], f[l][k][0] + f[k + 1][r][0]);
f[l][r][1] = Min(f[l][r][1], f[l][k][1] + f[k + 1][r][1]);
}
else {//乘号
int ans1 = f[l][k][0] * f[k + 1][r][0];
int ans2 = f[l][k][0] * f[k + 1][r][1];
int ans3 = f[l][k][1] * f[k + 1][r][0];
int ans4 = f[l][k][1] * f[k + 1][r][1];
//四种组合方式
f[l][r][0] = Max(f[l][r][0], Max(ans1, Max(ans2, Max(ans3, ans4))));
f[l][r][1] = Min(f[l][r][1], Min(ans1, Min(ans2, Min(ans3, ans4))));
}
}
}
}
return f[1][n][0];
}
法2(2倍法)
我们举个例子
有五个数:a, b, c, d, e
从a前切开并排成一列后,Ta是这样的
从b前切开并排成一列后,Ta是这样的
而我们会发现一个很有意思的东东,在这两个链中,都出现了:
那么我们能不能以此为契机,降低时间复杂度呢?
答案是肯定的!!!
我们将第一个链(其实从任何一个点切开后组成的链都可以,只是这样更符合我们的习惯)复制一份
则会得到a - b - c - d - e - a - b - c - d - e,记作*链
从a前切开后得到的链就是*链的第1个到第5个
从b前切开后得到的链就是*链的第2个到第6个
…
那我们只用对链进行“石子合并”了(因为链可以等效的代替原来各种方法的结果),答案就是*链中相隔4个的点之间进行“石子合并”后的最大值。
代码
#include <queue>
#include <cstdio>
using namespace std;
const int MAXN = 55;
const int INF = 0x3f3f3f3f;
int n;
int a[MAXN * 2];
int f[MAXN * 2][MAXN * 2][2];
//f[i][j][0]表示i~j合并后的最大值
//f[i][j][1]表示i~j合并后的最小值
bool s[MAXN * 2];
//s[i] = 0表示i与i-1的联结符号为+
queue<int> ans;
int Max(int x, int y) {return x > y ? x : y;}
int Min(int x, int y) {return x < y ? x : y;}
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
char tem[2];
scanf("%s", tem + 1);
if(tem[1] == 't') s[i] = 0;//加号
else s[i] = 1;
scanf("%d", &a[i]);
}
for(int i = 1; i <= n; i++) {
a[i + n] = a[i];
s[i + n] = s[i];
}
// for(int i = 1; i <= n * 2; i++) {
// printf("%d ", a[i]);
// if(i == n * 2) break;
// if(s[i + 1] == 0) printf("+ ");
// else printf("* ");
// }
// printf("\n");
for(int i = 1; i <= n * 2; i++) f[i][i][0] = f[i][i][1] = a[i];
for(int i = 1; i <= n; i++) {//区间长度
for(int j = 1; j <= n * 2 - i; j++) {//枚举左端点
int l = j, r = j + i;
f[l][r][0] = -INF;
f[l][r][1] = INF;
for(int k = l; k <= r - 1; k++) {//枚举断点
if(s[k + 1] == 0) {//加号
f[l][r][0] = Max(f[l][r][0], f[l][k][0] + f[k + 1][r][0]);
f[l][r][1] = Min(f[l][r][1], f[l][k][1] + f[k + 1][r][1]);
}
else {//乘号
int ans1 = f[l][k][0] * f[k + 1][r][0];
int ans2 = f[l][k][0] * f[k + 1][r][1];
int ans3 = f[l][k][1] * f[k + 1][r][0];
int ans4 = f[l][k][1] * f[k + 1][r][1];
f[l][r][0] = Max(f[l][r][0], Max(ans1, Max(ans2, Max(ans3, ans4))));
f[l][r][1] = Min(f[l][r][1], Min(ans1, Min(ans2, Min(ans3, ans4))));
}
}
}
}
// for(int i = 1; i <= n * 2; i++) {
// for(int j = i; j <= n * 2; j++) {
// printf("f[%d][%d][0] = %d\n", i, j, f[i][j][0]);
// printf("f[%d][%d][1] = %d\n", i, j, f[i][j][1]);
// }
// }
int _max = -INF;
for(int i = 1; i <= n; i++) {
if(f[i][i + n - 1][0] == _max) ans.push(i);
if(f[i][i + n - 1][0] > _max) {
_max = f[i][i + n - 1][0];
while(!ans.empty()) ans.pop();
ans.push(i);
}
// printf("%d %d\n", i, f[i][i + n - 1][0]);
}
printf("%d\n", _max);
while(!ans.empty()) printf("%d ", ans.front()), ans.pop();
return 0;
}