动规之删数问题

版权声明:写的不对的地方希望大家能“狠狠地”指出 https://blog.csdn.net/qq_37006625/article/details/84799532

删数问题

问题描述
现有n个正整数组成的序列a,从中删除一个数,得分是其本身同左、右相邻的数的乘积,
然后再在剩余的整数中继续删除,注意序列两端的数字a1和an是不能删除的,求这样删除n-2个整数后的最大得分。

例如有四个数3 、4、5、6,按照先4后5的删除顺序,其得分为345+356=150,
按照先5后4的删除顺序,其得分为456+346=192,因此最大得分为192。

测试样例:

  • 第一组

    4
    3 4 5 6

  • 第二组

    5
    3 6 7 8 2

问题分析
----又是一个典型的区间动规
最优子结构:

dp[i][j] = dp[i][k] + dp[k][j] + a[i] * a[k] * a[j];

对于第i个物品到第j个物品,假设最后删掉k得到的结果最大,那么最后一次删除时,得到的分数就是 a[i] * a[k] * a[j]。那么总的得分就是需要加上之前删掉k的左右两边除了i,j之外所有数的和,即dp[i][j] 的两个子问题,分别是dp[i][k] 和dp[k][j]。由此便可得出上面所写的最优子结构

代码

扫描二维码关注公众号,回复: 4439976 查看本文章
/*
删数问题: 
现有n个正整数组成的序列a,从中删除一个数,得分是其本身同左、右相邻的数的乘积,
然后再在剩余的整数中继续删除,注意序列两端的数字a1和an是不能删除的,求这样删除n-2个整数后的最大得分。

例如有四个数3 、4、5、6,按照先4后5的删除顺序,其得分为3*4*5+3*5*6=150,
按照先5后4的删除顺序,其得分为4*5*6+3*4*6=192,因此最大得分为192。

测试样例: 

4
3 4 5 6
 
5
3 6 7 8 2 

*/ 
#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;

#define MAX_N 1010

static int a[MAX_N];//用来保存序列 
static int dp[MAX_N][MAX_N];//i  j  用来保存删除第i个数到第j个数所得到的最优值 


void dp1(int n)
{
	int j = 0;
	for(int r = 3;r <= n;r ++ ){
		for(int i = 1;;i ++ ){
			int j = i + r -1;
			for(int k = i + 1;k <= j - 1;k ++ ){
				dp[i][j] = max(dp[i][j],dp[i][k]+dp[k][j]+a[i]*a[k]*a[j]);
			}
			if(j >= n) break;
		}
	} 
	cout << "删除n-2个数之后,得到的最大值为:" << endl;
	cout << dp[1][n] << endl;
}

int main()
{
	int n;
	cout << "请输入序列的位数n" << endl;
	while(cin >> n,n){
		for(int i = 1;i <= n;i ++ ){
			cin >> a[i];
		}
		memset(dp,0,MAX_N*MAX_N);
		for(int i = 1;i <= n - 1;i ++ ){
			dp[i][i + 1] = 0;
		}
		dp1(n);
		cout << "请输入序列的位数n" << endl;
	}  
	return 0;
} 

猜你喜欢

转载自blog.csdn.net/qq_37006625/article/details/84799532