算法分析:
初看起来,就是简单的区间dp,于是很顺利打出来,结果有些点过不了。百思不得其解。下面是初始代码。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
int n, dotval[110], f[110][110];
char edgval[110];
int main() // 没有考虑负数,不全面
{
scanf("%d", &n);
//使用格式化输入scanf混读%c和%d有点问题,数据不大,可以用cin
for (int i = 1; i <= n; ++i)
{
cin>>edgval[i]>>dotval[i];
edgval[i+n] = edgval[i];
dotval[i+n] = dotval[i];
}
// 将环拆成链后,每个点前面的那条边是属于该点的边
n *= 2;
memset(f, 0xcf, sizeof(f));
for (int i = 1; i <= n; ++i) f[i][i] = dotval[i];
for (int len = 2; len <= n; ++len)
for (int i = 1; i < n; ++i)
{
int j = i + len - 1;
if (j > n) continue;
for (int k = i; k < j; ++k)
{
int tem;
if (edgval[k+1] == 't') tem = f[i][k] + f[k+1][j];
else tem = f[i][k] * f[k+1][j];
f[i][j] = max(f[i][j], tem);
}
}
int ans = -1e9;
n /= 2;
for (int i = 1; i <= n; ++i)
if (f[i][i+n-1] > ans) ans = f[i][i+n-1];
printf("%d\n", ans);
for (int i = 1; i <= n; ++i)
if (f[i][i+n-1] == ans) printf("%d ", i);
printf("\n");
return 0;
}
看书上的讲解,原来是没有考虑负数的问题。状态
f
[
i
]
[
j
]
f[i][j]
f[i][j],如果是加号,没有问题。但如果是乘号的话,是不能简单地有
f
[
i
]
[
k
]
∗
f
[
k
+
1
]
[
j
]
f[i][k]*f[k+1][j]
f[i][k]∗f[k+1][j]转移过来的。因为两个负数相乘也许是一个超过两个最大值的数。状态
f
m
a
x
[
i
]
[
j
]
f_max[i][j]
fmax[i][j]表示区间
[
i
,
j
]
[i,j]
[i,j]的最大值,
g
m
i
n
[
i
]
[
j
]
g_min[i][j]
gmin[i][j]表示最小值。则对于乘号来讲:
f
m
a
x
[
i
]
[
j
]
=
m
a
x
(
f
m
a
x
[
i
]
[
k
]
∗
f
m
a
x
[
k
+
1
]
[
j
]
,
f
m
a
x
[
i
]
[
k
]
∗
g
m
i
n
[
k
+
1
]
[
j
]
,
g
m
i
n
[
i
]
[
k
]
∗
f
m
a
x
[
k
+
1
]
[
j
]
,
g
m
i
n
[
i
]
[
k
]
∗
g
m
i
n
[
k
+
1
]
[
j
]
)
fmax[i][j] = max(fmax[i][k]*fmax[k+1][j],fmax[i][k]*gmin[k+1][j],gmin[i][k]*fmax[k+1][j],gmin[i][k]*gmin[k+1][j])
fmax[i][j]=max(fmax[i][k]∗fmax[k+1][j],fmax[i][k]∗gmin[k+1][j],gmin[i][k]∗fmax[k+1][j],gmin[i][k]∗gmin[k+1][j])
g
m
i
n
[
i
]
[
j
]
=
m
i
n
(
f
m
a
x
[
i
]
[
k
]
∗
f
m
a
x
[
k
+
1
]
[
j
]
,
f
m
a
x
[
i
]
[
k
]
∗
g
m
i
n
[
k
+
1
]
[
j
]
,
g
m
i
n
[
i
]
[
k
]
∗
f
m
a
x
[
k
+
1
]
[
j
]
,
g
m
i
n
[
i
]
[
k
]
∗
g
m
i
n
[
k
+
1
]
[
j
]
)
gmin[i][j] = min(fmax[i][k]*fmax[k+1][j],fmax[i][k]*gmin[k+1][j],gmin[i][k]*fmax[k+1][j],gmin[i][k]*gmin[k+1][j])
gmin[i][j]=min(fmax[i][k]∗fmax[k+1][j],fmax[i][k]∗gmin[k+1][j],gmin[i][k]∗fmax[k+1][j],gmin[i][k]∗gmin[k+1][j])
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
int n, dotval[110], f_max[110][110], g_min[110][110];
char edgval[110];
int main()
{
scanf("%d", &n);
//使用格式化输入scanf混读%c和%d有点问题,数据不大,可以用cin
for (int i = 1; i <= n; ++i)
{
cin>>edgval[i]>>dotval[i];
edgval[i+n] = edgval[i];
dotval[i+n] = dotval[i];
}
// 将环拆成链后,每个点前面的那条边是属于该点的边
n *= 2;
memset(f_max, 0xcf, sizeof(f_max)); // 极小值
memset(g_min, 0x3f, sizeof(g_min)); // 极大值
for (int i = 1; i <= n; ++i) f_max[i][i] = g_min[i][i] = dotval[i];
for (int len = 2; len <= n; ++len)
for (int i = 1; i < n; ++i)
{
int j = i + len - 1;
if (j > n) continue;
for (int k = i; k < j; ++k)
{
int temmax = -1e9, temmin = 1e9;
if (edgval[k+1] == 't')
{
f_max[i][j] = max(f_max[i][j], f_max[i][k] + f_max[k+1][j]);
g_min[i][j] = min(g_min[i][j], g_min[i][k] + g_min[k+1][j]);
}else
{
temmax = max(temmax, f_max[i][k] * f_max[k+1][j]);
temmax = max(temmax, f_max[i][k] * g_min[k+1][j]);
temmax = max(temmax, g_min[i][k] * f_max[k+1][j]);
temmax = max(temmax, g_min[i][k] * g_min[k+1][j]);
temmin = min(temmin, f_max[i][k] * f_max[k+1][j]);
temmin = min(temmin, f_max[i][k] * g_min[k+1][j]);
temmin = min(temmin, g_min[i][k] * f_max[k+1][j]);
temmin = min(temmin, g_min[i][k] * g_min[k+1][j]);
f_max[i][j] = max(f_max[i][j], temmax);
g_min[i][j] = min(g_min[i][j], temmin);
}
}
}
int ans = -1e9;
n /= 2;
for (int i = 1; i <= n; ++i)
if (f_max[i][i+n-1] > ans) ans = f_max[i][i+n-1];
printf("%d\n", ans);
for (int i = 1; i <= n; ++i)
if (f_max[i][i+n-1] == ans) printf("%d ", i);
printf("\n");
return 0;
}
总结与反思:
- 还是审题不清。没有注意到负数的问题。
- 破环成链是解决环上问题的经典方法。