P4342 [IOI1998]Polygon
P4342 [IOI1998]Polygon
题目太长了,本人表示不想粘了(懒)。
一句话题意:
给你一个环,让你删除环上一条边之后,把其他的点按一定顺序组合起来,每次操作有一定的权值,问你怎么
操作使权值最大。
这是典型的合并模型,和合并果子以及能量项链比较相似。
按照套路,我们设 \(f[l][r]\) 表示从 \(l\) 合并到 \(r\) 的最大权值。
就有转移方程 \(f[l][r] = max(f[l][r],f[l][k] op f[k+1][r], op \in{+,x})\)
三层 \(for\) 循环就可以搞定。
但当你写完后,你却发现你输出的答案要比样例小得多。
这说明,我们的思路出了问题。
举个简单的例子,\(-10000 \times -100000\) 要比 \(10 \times 10\) 是要大的。
也就是说当我们两个最小值为负数时相乘的结果肯能会比两个最大值相乘的结果还要大。
因此,我们不能只记录最大值,我们还要记录一下最小值。
重新设一下状态 \(f[l][r][0]\) 表示从 \(l\) 合并到 \(r\) 的最大权值,\(f[l][r][1]\) 为从 \(l\) 合并到 \(r\) 的最小权值。
如果符号是 加号的话,最大值等于两个区间的最大值相加,最小值同理
-
\(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[k+1][r][1])\)
如果是乘号的话就需要考虑很多种情况。第一个区间最大值(最小值)乘以第二个区间最大值(最小值)
然后再从这四种取值中选一个最大值和最小值来继承就可以了。
然后我们采用区间 \(dp\) 一个比较常用的方法---断环成链。
\(f[i][i+n-1][0/1]\) 就相当于去掉第 \(i\) 条边的合并能得到的最大权值(可以感性理解一下)
最后的答案就是 \(max(f[i][i+n-1])\)
Code:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int inf = 2147483647;
int n,maxn,cnt,f[220][220][2],id[220];
char opt[220];
int main()
{
scanf("%d",&n);
for(int i = 1; i <= n; i++)
{
cin>>opt[i];
scanf("%d",&f[i][i][0]);
f[i][i][1] = f[i][i][0];
opt[i+n] = opt[i];
f[n+i][n+i][0] = f[n+i][n+i][1] = f[i][i][0];//断环成链
}
for(int len = 2; len <= n; len++)
{
for(int L = 1; L + len - 1 <= n * 2; L++)
{
int R = L + len - 1;
f[L][R][0] = -inf; f[L][R][1] = inf;
for(int k = L; k < R; k++)//大力转移
{
if(opt[k+1] == 't')
{
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]);
}
if(opt[k+1] == 'x')
{
f[L][R][0] = max(f[L][R][0],max(f[L][k][0] * f[k+1][R][0],f[L][k][1] * f[k+1][R][1]));
f[L][R][0] = max(f[L][R][0],max(f[L][k][0] * f[k+1][R][1],f[L][k][1] * f[k+1][R][0]));
f[L][R][1] = min(f[L][R][1],min(f[L][k][0] * f[k+1][R][1],f[L][k][1] * f[k+1][R][0]));
f[L][R][1] = min(f[L][R][1],min(f[L][k][0] * f[k+1][R][0],f[L][k][1] * f[k+1][R][1]));
}
// cout<<len<<" "<<L<<" "<<R<<" "<<f[L][R][0]<<" "<<f[L][R][1]<<endl;
}
}
}
for(int i = 1; i <= n; i++)
{
if(f[i][i+n-1][0] > maxn)//记录一下答案
{
maxn = f[i][i+n-1][0];
cnt = 0;
id[++cnt] = i;
}
else if(f[i][i+n-1][0] == maxn) id[++cnt] = i;
}
printf("%d\n",maxn);
for(int i = 1; i <= cnt; i++) printf("%d ",id[i]);
return 0;
}