题意:给定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;
}