【破环成链】【区间dp】LGP4342 [IOI1998]Polygon
【破环成链】【区间dp】LGP4342 [IOI1998]Polygon
题目可能有些许修改,但大意一致
多边形是一个玩家在一个有n个顶点的多边形上的游戏,如图所示,其中n=4。每个顶点用整数标记,每个边用符号+(加)或符号*(乘积)标记。
第一步,删除其中一条边。随后每一步:
选择一条边连接的两个顶点V1和V2,用边上的运算符计算V1和V2得到的结果来替换这两个顶点。
游戏结束时,只有一个顶点,没有多余的边。
如图所示,玩家先移除编号为3的边。之后,玩家选择计算编号为1的边,然后计算编号为4的边,最后,计算编号为2的边。结果是0。
(翻译者友情提示:这里每条边的运算符旁边的数字为边的编号,不拿来计算)
编写一个程序,给定一个多边形,计算最高可能的分数。
输入格式
输入描述一个有n个顶点的多边形,它包含两行。第一行是数字n,为总边数。
第二行描述这个多边形,一共有2n个读入,每两个读入中第一个是字符,第二个是数字。
第一个字符为第一条边的计算符号(t代表相加,x代表相乘),第二个代表顶点上的数字。首尾相连。
3 < = n < = 50
对于任何一系列的操作,顶点数字都在[-32768,32767]的范围内。
输出格式
第一行,输出最高的分数。在第二行,它必须写出所有可能的被清除后的边仍能得到最高得分的列表,必须严格递增。
感谢@2016c01 提供的翻译
输入输出样例
输入 #1
4
t -7 t 4 x 2 x 5
很明显这里t表示这条边是加法边,然后后面那个数字表示这条边指向的结点的权值。
然后x代表乘法边。
输出 #1
33
1 2
题意
首先定义边的类型为加或者乘,然后每次可以选择一条边把两边的结点合并,合并运算就是结点内数字对应边的类型的运算。
比如说权值为1的点和权值为2的点通过加法边可以合并为的一个点,继承原来点点所有边。
现在给出一个个点条边的简单环,求进行次操作后剩下那个点的点权最大值。
思路
进行次操作,就相当于舍弃掉一条边,而剩下的边必须在区间上连续。
那么我们首先也是要破环成链,复制一份放在后面
然后又是合并问题了。
定义表示把区间的点合起来的最大权值,同样枚举中转点。
这里要慢一点,我们设边的类型为,终点为第个点
合并区间和区间用的就是
然后合并的时候需要两边都有东西,所以
所以状态转移方程:
初始状态:
结束状态:因为是只进行了次操作,但是合并了个点,所以是
转移顺序:
- 区间长度升序
- 区间起点升序,同时定义区间终点
- 中转点升序
注意到有负数所以数组其他元素初始值要是负数的极小值
打完回来
然后发现漏考虑了一些情况。
这里是有负数的。
是有负数乘法的。。。
负数乘以负数负负得正。
所以负数下乘数越小乘积越大。
所以我们不能只记录一个最大值,还需要记录一个最小值。
再分别讨论一下。
对于加法,肯定是小的加小的得到最小的,大的加大的得到最大的。
至于乘法,最小的可能是两个最小的相乘,也可能是最大的和最小的相乘,还可能是最大的和最小的相乘。。。
但是最大的就只有俩情况:最大和最大,最小和最小。
对于上述的五种情况,分别取个最值就可以维护最大值和最小值了
(真的有被坑到,不愧是IOI的题)
总结一下,整个题需要俩数组表示合成的最大值,表示最小值,然后有
答案就是
Code
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N=128;
inline const int x(const int a,const int b)
{
return a*b;
}
inline const int t(const int a,const int b)
{
return a+b;
}
const int(*p[N])(const int,const int);
int val[N],dp[N][N],mn[N][N];
char tpe[N];
int nn,n,ans;
int main()
{
cin>>nn;
memset(dp,0x80,sizeof(dp));
memset(mn,0x01,sizeof(mn));
for(register int i=1;i<=nn;++i) cin>>tpe[i]>>val[i];
for(register int i=1;i<=nn;++i) tpe[i+nn]=tpe[i];
for(register int i=1;i<=nn;++i) val[i+nn]=val[i];
n=nn<<1;
for(register int i=1;i<=n;++i) p[i]=tpe[i]=='t'? t:x;
for(register int i=1;i<=n;++i) mn[i][i+1]=dp[i][i+1]=val[i];
for(register int len=2;len<=n;++len)
{
for(register int i=1;i<=n-len+1;++i)
{
const int j=i+len;
for(register int k=i+1;k<j;++k)
{
dp[i][j]=max(dp[i][j],(*p[k])(dp[i][k],dp[k][j]));
dp[i][j]=max(dp[i][j],(*p[k])(mn[i][k],mn[k][j]));
mn[i][j]=min(mn[i][j],(*p[k])(mn[i][k],mn[k][j]));
mn[i][j]=min(mn[i][j],(*p[k])(mn[i][k],dp[k][j]));
mn[i][j]=min(mn[i][j],(*p[k])(dp[i][k],mn[k][j]));
}
}
}
ans=dp[0][0];
for(register int i=0;i<nn;++i)
{
ans=max(ans,dp[1+i][nn+i+1]);
}
cout<<ans<<endl;
for(register int i=0;i<nn;++i)
{
if(ans==dp[1+i][nn+i+1])
{
printf("%d ",i+1);
}
}
printf("\n");
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具