【破环成链】【区间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的点通过加法边可以合并为1+2=3的一个点,继承原来点点所有边。
现在给出一个3n50个点n条边的简单环,求进行n1次操作后剩下那个点的点权最大值。

思路

进行n1次操作,就相当于舍弃掉一条边,而剩下的边必须在区间上连续。
那么我们首先也是要破环成链,复制一份放在后面
然后又是合并问题了。
定义dpi,j表示把区间[i,j)的点合起来的最大权值,同样枚举中转点。
这里要慢一点,我们设边k的类型为k,终点为第k个点
合并区间[i,k)和区间[k,j)用的就是k
然后合并的时候需要两边都有东西,所以k(i,j)
所以状态转移方程:

dpi,j=maxi<k<j{(dpi,k)k(dpk,j)}(0<i<jn+1)

初始状态:dpi,i+1=vali
结束状态:因为是只进行了n1次操作,但是合并了n个点,所以是max0i<n{dp1+i,n+i+1}
转移顺序:

  1. 区间长度升序len:[2,n]
  2. 区间起点升序i:[1,nlen+1],同时定义区间终点j=i+len
  3. 中转点升序k:(i,j)

注意到有负数所以dp数组其他元素初始值要是负数的极小值


打完回来
80pts
然后发现漏考虑了一些情况。
这里是有负数的。
是有负数乘法的。。。
负数乘以负数负负得正。
所以负数下乘数越小乘积越大。
所以我们不能只记录一个最大值,还需要记录一个最小值。
再分别讨论一下。
对于加法,肯定是小的加小的得到最小的,大的加大的得到最大的。
至于乘法,最小的可能是两个最小的相乘,也可能是最大的和最小的相乘,还可能是最大的和最小的相乘。。。
但是最大的就只有俩情况:最大和最大,最小和最小。
对于上述的五种情况,分别取个最值就可以维护最大值和最小值了
(真的有被坑到,不愧是IOI的题)
总结一下,整个题需要俩数组dpi,j表示合成[i,j)的最大值,mni,j表示最小值,然后有

dpi,j=maxi<k<j{(dpi,k)k(dpk,j),(mni,k)k(mnk,j)}mni,j=mini<k<j{(mni,k)k(mnk,j),(mni,k)k(dpk,j),(dpi,k)k(mnk,j)}

答案就是max0i<n{dp1+i,1+n+i}

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;
}
posted @   IdanSuce  阅读(66)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示