默罕默德的炸弹

Description

炸弹是塔利班对付美军的武器之一。默罕默德在塔利班的主要任务是做炸弹。默罕默德所做的新型炸弹由若干个小炸弹组成,每个小炸弹有一个威力值,整个炸弹的威力值是组成这个炸弹的最大威力差的平方,即(max-min)^2,假设一个炸弹有5个小炸弹组成,威力分别为5 9 8 2 1,那么它的威力为(9-1)^2=64。 
时间紧任务多默罕默德的师傅兼老爸老默罕默德又不在,默罕默德不敢调整小炸弹的顺序,只能确定它们的分组。请你帮助默罕默德确定小炸药的分组,使制造出的炸弹拥有最大的威力和。

Input

第一行有 1 个正整数N( 1 <= N <= 1000),表示有N个小炸弹。 
第二行有N个数,其中第i个数Ai表示第i个小炸弹的威力值( 0 <= Ai <= 1000)。

Output

输出炸弹的威力和。

Sample Input

6
5 9 8 2 1 6

Sample Output

77

思路:

题意是不能改变炸弹顺序,只能确定它们的分组,使制造出的炸弹拥有最大的威力和。

方便理解题意,那么先看下样例是怎么过的

5 9 8 2 1 6

一组数据有多种分法:

你可以分成1组,那么威力和就是只分成一组的威力(9-1)²=64。

也可以分成2组,如(5,9) (8,2,1,6)这样威力和是16+49=65,分成2组还有别的组合这里就不算了。

我们可以知道6个数 最多能分成6组(每个数为一组)。

样例的最优分组是 (5,9) (8,2) (1,6)。3组,这样威力和是16+35+25=77最大。

 

开始分析 :看上去,好像能用暴力,但是仔细分析就觉得不靠谱了。1000个数就有

C(0,999)+C(1,999)+C(2,999)+……+C(999,999)种可能。这就只能呵呵了。

换种思路:

有n个小炸弹 可以能分成i组(1<=i<=n)。

如果我们已经知道(1<=i<=n)dp[i][n]表示:n个小炸弹,分成i组的各种组合中,(威力和最大的那个组合)的(威力和)。如样例中dp[1][6]=64 . dp[3][6]=77.

那么我只要输出(1<=i<=n)中dp[i][n]最大的那个就行了。

  

那么现在的问题变成如何求dp[i][n].想到这里已经明白显然是个动态规划问题,要去想如何进行状态转移。

dp[i][n]讨论的是把n个数分成i组的问题。为了方便说明,继续拿样例讨论。

比如 5 9 8 2 1 6 求dp[3][6].此时我们已经算出最优解的情况是(5,9) (8,2) (1,6). 这时最后一组是(1,6)

但是一开始我不知道最优解的最后一组是6,还是(1,6),还是(2,1,6)等。

为什么要知道最后一组呢?

假设最后一组是6,那么问题就变成求5 9 8 2 1中分成2组的最大威力和了。

此时他的威力和是 dp[2][5]+v[6][6].      //假设我们已经知道v[i][j]代表下标i到j为一组的威力和.

同理一样如果最后一组是(1,6),那么问题就变成求5,9,8,2中分成2组的最大威力和。

此时他的威力和是dp[2][4]+v[5][6],

我们只要枚举这样所有的可能,就能找出最优解得到dp[3][6]。

我们知道,若最后一组知道,那么扣掉最后一组的数,假设还剩k个数,k显然不能等于n.

这k个数要分成i-1组显然k要大于i-1。那么i-1<=k<n.

此时已经可以推出转移方程:dp[i][j]=max(dp[i][j],dp[i-1][k]+v[k+1][j]); (i-1<=k<n)

剩下的把v[i][j]搞定就好了。

#include <cstdio>
#include <algorithm>
using namespace std;
#define max(a,b) (((a)>(b))?(a):(b))
int n,a[1005],dp[1005][1005],v[1005][1005];
int main()
{
	int i,j,k,res;
	int iMax,iMin;
	scanf("%d",&n);
	for(i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
	}
	for(i=1;i<=n;i++)
	{
		iMax=iMin=a[i];
		for(j=i;j<=n;j++)
		{
			if(a[j]>iMax) iMax=a[j];
			if(a[j]<iMin) iMin=a[j];
			v[i][j]=(iMax-iMin)*(iMax-iMin);
		}
	}//v[i][j]代表从下标i到j为一组的炸弹威力
	//dp i=1;
	for(j=1;j<=n;j++) dp[1][j]=v[1][j];
	// i>=2;
	for(i=2;i<=n;i++) 
	{
		for(j=i;j<=n;j++)
		{
			for(k=i-1;k<j;k++)
			{
				dp[i][j]=max(dp[i][j],dp[i-1][k]+v[k+1][j]);
			}
		}
	}
	res=0;
	for(i=1;i<=n;i++)
	{
		res=max(res,dp[i][n]);
	}
	printf("%d\n",res);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值