POJ1651-类似矩阵连乘问题

题意:给定n个数,每次取其中一个数(第一个数和最后一个数不能取),得分+=这个数乘以它左边的数和右边的数。要求最后的得分最小。

思路:因为开始知道这个题的DP方程和矩阵连乘的DP方程一样,所以一直根据方程想思路,结果想了很久都没想出来,最后看了评论区的大神提示,瞬间想通。看来自己实力还是很渣渣啊。。唉

我们假设只有a1,a2,a3三个数,那我们只能取a2,那最后的得分=a1*a2*a3;  假如有n个数,设k为最后一个取出来的数,那么,最后一次的得分只与a1,an,ak有关,那么我们把整个区间以k分成两段,左边一段的最后一次得分必然为a1*ak*ai(i>1 &&i<k),也就是说左边的得分与右边怎么取无关,

那么我们总得分dp[1][n]=dp[1][k]+dp[k][n]+a[1]*a[k]*a[n];

于是我们可以得出递归方程dp[i][j]=dp[i][k]+dp[k][i]+a[i]*a[k]*a[j]. 这个方程和矩阵连乘问题的方程基本一样。那么我们可以写下如下代码

#include "iostream"
using namespace  std;
#define  len 110
int main()
{
	int  n;
	int a[len];
	int dp[len][len];//dp[1][n]是最后结果 ,dp[i][j]=dp[i][k]+dp[k+1][j]+p*q
	int i,j,k;
	scanf("%d",&n);
	for (i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
	}
	//递归公式 dp[i][j]=min{dp[i][k]+dp[k+1][j]+k*i*j,k>=i  k<j}
	for (i=1;i<=n;i++)
	{
		memset(dp[i],0,sizeof(dp[i]));
		dp[i][i+2]=a[i]*a[i+1]*a[i+2];
	}
	for (int m=1;m<=n;m++)
	{
		for (i=1;i<=n;i++)
		{
			j=i+3+m;
			for (j=i+3;j<=n;j++)
			{
				int min=9999999;//注意这个数要取大点
				for (k=i+1;k<j;k++)
				{
					if(min>dp[i][k]+dp[k][j]+a[k]*a[i]*a[j])
					{
						min=dp[i][k]+dp[k][j]+a[k]*a[i]*a[j];
					}
				}
				dp[i][j]=min;
			}
		}
	}
	
	printf("%d\n",dp[1][n]);
	return 0;
}

 

你可能感兴趣的:(poj)