POJ 1179 :Polygon (区间DP + 环形处理)

本文介绍了一种在环状结构中使用动态规划解决问题的方法。该问题涉及在一个带有运算符号的多边形中,通过合理删除边并进行节点合并来最大化最终节点的权值。文章详细阐述了解决方案的设计思路,并提供了完整的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在这里插入图片描述
题目大意:有一个n边形,每条边有一个运算符号,每个点上有一个权值,一开始可以选择一条边删除,以后每次选择一条边将一条边的两个点合成一个点,这个点的权值为这两个点的权值与边的操作运算的结果。最后只有一个点,输出这个点的最大分值,以及删除哪些边可以得到这个分值。

分析:可以枚举删除哪条边,然后进行处理。
nnn = 222 的时候,最大值只能是这条边的两点运算。当n>2n > 2n>2时,最后的答案和删除边的顺序有一定关系。最后删除的那一条边的左右两端是两个独立的子问题,删除顺序互相不影响。而删除这条边得到的最大分值具有最优子问题结构:显然加法时两端尽量大答案最大,乘法较为复杂要分析两端最小的情况,因为存在负数结果,负数乘负数得正数。

这是一个类似石子合并的过程,但是它成环了,对于环可以将环断开,拷贝补成一个 2n2 n2n 长的链。
(尝试直接在环上DP感觉特别难写,放弃)
当拆成链的时候,复杂度下降了一维,变成了 n3n ^ 3n3,因为[i,i+n−1][i,i + n -1][i,i+n1]的答案相当于删除了第 (imod  n)(i \mod n)(imodn) 个点和第 ((i+n−1)mod  n)((i + n - 1) \mod n)((i+n1)modn)个点之间的边

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;
int n,m;
const int maxn = 1e2 + 10;
int dp[maxn][maxn][2];
int a[maxn][maxn];
char s[2];
int tmp[maxn];
int sta[maxn];
int ans[maxn],res,cnt;
int vis[maxn];
int main() {
	scanf("%d",&n);
	for(int i = 0; i < 2 * n; i++) {
		if(i % 2 == 0) {
			scanf("%s",s);
			tmp[i] = s[0];
		}
		else {
			scanf("%d",&tmp[i]);
			sta[i / 2] = tmp[i];
		}
	}
	for(int i = 1; i < 2 * n; i += 2) {
		int u = i, v = (i + 2) % (2 * n),p = (i + 1) % (2 * n);
		a[u / 2][v / 2] = tmp[p];
		a[v / 2][u / 2] = tmp[p];
	}
	//环处理,扩展成一条链 
	for(int i = 0; i < n; i++) {
		sta[i + n] = sta[i];
		a[i + n - 1][i + n] = a[(i + n - 1) % n][i];
		a[i + n][i + n - 1] = a[i + n - 1][i + n];
		dp[i][i][0] = dp[i][i][1] = dp[i + n][i + n][0] = dp[i + n][i + n][1] = sta[i];
	}
	n *= 2;
	res = -0x3f3f3f3f;
	for(int s = 2; s <= n / 2; s++) {
		for(int l = 0; l + s - 1 < n; l++) {
			int r = l + s - 1;
			dp[l][r][0] = -0x3f3f3f3f;
			dp[l][r][1] = 0x3f3f3f3f;
			for(int k = l; k < r; k++) {
				if(a[k][k + 1] == 't') {
					dp[l][r][0] = max(dp[l][r][0],dp[l][k][0] + dp[k + 1][r][0]);
					dp[l][r][1] = min(dp[l][r][1],dp[l][k][1] + dp[k + 1][r][1]);
				}
				else {
					dp[l][r][0] = max(dp[l][r][0],dp[l][k][0] * dp[k + 1][r][0]);
					dp[l][r][0] = max(dp[l][r][0],dp[l][k][1] * dp[k + 1][r][1]);
					dp[l][r][0] = max(dp[l][r][0],dp[l][k][1] * dp[k + 1][r][0]);
					dp[l][r][0] = max(dp[l][r][0],dp[l][k][0] * dp[k + 1][r][1]);
					
					dp[l][r][1] = min(dp[l][r][1],dp[l][k][1] * dp[k + 1][r][0]);
					dp[l][r][1] = min(dp[l][r][1],dp[l][k][0] * dp[k + 1][r][1]);
					dp[l][r][1] = min(dp[l][r][1],dp[l][k][1] * dp[k + 1][r][1]);
					dp[l][r][1] = min(dp[l][r][1],dp[l][k][0] * dp[k + 1][r][0]);
				}
			}
			
			if(s == n / 2) {
				if(res == dp[l][r][0]) {
					if(vis[l % (n / 2) + 1]) continue;
					vis[l % (n / 2) + 1] = 1;
					ans[++cnt] = l % (n / 2) + 1;
				}
				else if(res < dp[l][r][0]) {
					cnt = 0;
					memset(vis,0,sizeof vis);
					ans[++cnt] = l % (n / 2) + 1;
					res = dp[l][r][0];
					vis[l % (n / 2) + 1] = 1;
				}
			}
		}
	}
	printf("%d\n",res);
	for(int i = 1; i <= cnt; i++) {
		if(i - 1) printf(" ");
		printf("%d",ans[i]);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值