283. 多边形

算法分析:
初看起来,就是简单的区间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;
}

总结与反思:

  1. 还是审题不清。没有注意到负数的问题。
  2. 破环成链是解决环上问题的经典方法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值