能量项链问题

//题目:

/*【问题描述】

Mars星球上,每个Mars人都随身佩带着一串能量项链。在项链上有N颗能量珠。能量珠是一颗有头标记与尾标记的珠子,这些标记对应着某个正整数。并且,对于相邻的两颗珠子,前一颗珠子的尾标记一定等于后一颗珠子的头标记。因为只有这样,通过吸盘(吸盘是Mars人吸收能量的一种器官)的作用,这两颗珠子才能聚合成一颗珠子,同时释放出可以被吸盘吸收的能量。如果前一颗能量珠的头标记为m,尾标记为r,后一颗能量珠的头标记为r,尾标记为n,则聚合后释放的能量为m*r*nMars单位),新产生的珠子的头标记为m,尾标记为n

需要时,Mars人就用吸盘夹住相邻的两颗珠子,通过聚合得到能量,直到项链上只剩下一颗珠子为止。显然,不同的聚合顺序得到的总能量是不同的,请你设计一个聚合顺序,使一串项链释放出的总能量最大。

例如:设N=44颗珠子的头标记与尾标记依次为(23) (35) (510) (102)。我们用记号⊕表示两颗珠子的聚合操作,(jk)表示第jk两颗珠子聚合后所释放的能量。则第41两颗珠子聚合后释放的能量为:

(41)=10*2*3=60

这一串项链可以得到最优值的一个聚合顺序所释放的总能量为

((41)2)3=10*2*3+10*3*5+10*5*10=710

【输入文件】

输入文件energy.in的第一行是一个正整数N4N100),表示项链上珠子的个数。第二行是N个用空格隔开的正整数,所有的数均不超过1000。第i个数为第i颗珠子的头标记(1iN),当i<N时,第i颗珠子的尾标记应该等于第i+1颗珠子的头标记。第N颗珠子的尾标记应该等于第1颗珠子的头标记。

至于珠子的顺序,你可以这样确定:将项链放到桌面上,不要出现交叉,随意指定第一颗珠子,然后按顺时针方向确定其他珠子的顺序。

【输出文件】

输出文件energy.out只有一行,是一个正整数EE2.1*109),为一个最优聚合顺序所释放的总能量。

【输入样例】

4

2  3  5  10

【输出样例】

710

*/

/*解题思路:DP算法。

我们设有n颗石子,f[i,j]代表从第i颗到第j颗的最大值(1<=i<=j<=n)。

定义一个结构体

struct stone

{int front,rear;};

Stone b[n];//n个石头

f[i,j]=f[i,k]+f[k+1,j]+b[i].front*b[k].rear*b[j].rear;

其中我们假设i~kk+1~j颗已经聚合,此时就剩两颗。来求其最大值。

那么上式就是要求的状态转移方程。

*/

/*心得:动规比想象的要有趣,找状态方程是种乐趣,最后将式子用程序实现又要花功夫,其实多做两道就熟练了。呵呵有点儿小小的受虐倾向!

*/

//说明:程序要求读写文件,在这没有加上。还有做了测试了几组数据觉得没问题就上传了,开始用的贪心法算的错了。如:3 2 8 7 5 1 4 10 13 2 6贪心法结果:4832,动规:4870

#include<cstdio>

struct stone//define a sturct to express the head and rear of the stone.
{
	int front,rear;
};

void initial(int **f,int n)
{
	int i,j;
	for(i=0; i<=n; i++)
		for(j=0; j<=n; j++)
			f[i][j]=0;
}

int Processe(int **f, stone* b, int n, int r)
{
	int i,j,k;
	for(j=2; j<=n; j++)
	{
		for(i=j-1; i>=1; i--)
		{
			k=i;
			int t=f[i][k]+f[k+1][j]+b[i+r].front*b[k+r].rear*b[j+r].rear;
			for(k=i+1; k<j; k++)
			{
				f[i][j]=f[i][k]+f[k+1][j]+b[i+r].front*b[k+r].rear*b[j+r].rear;
				if(t<f[i][j])//look for the max of f[i][j],which comes from state transfer equation.
					t=f[i][j];
			}
			f[i][j]=t;
		}
	}
	return f[1][n];
}

int main()
{
	int n;
	while(scanf("%d",&n)!=EOF)
	{
		stone *b;
		b=new stone[2*n+1];//申请两倍为了方便后面划分,一个环要在每个点切一刀计算出每种可能情况并比较
		int i;
		scanf("%d",&b[1].front);
		for(i=2; i<=n; i++)
		{
			scanf("%d",&b[i].front);
			b[i-1].rear=b[i].front;
		}
		b[n].rear=b[1].front;
		for(; i<=2*n; i++)
			b[i]=b[i-n];

		int **f;
		f=new int*[n+1];
		for(i=0; i<=n; i++)
			f[i]=new int [n];
		
		int max,t;
		initial(f,n);
		max=Processe(f,b,n,0);
		for(i=1; i<n; i++)
		{
			initial(f,n);
			t=Processe(f,b,n,i);//加入参数i就是切分环用的
			if(t>max)
				max=t;
		}
		printf("%d\n",max);
	}
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值