poj1651
题目链接:http://poj.org/problem?id=1651
题的大意是:给出一组N个数,每次从中抽出一个数(第一和最后一个不能抽),该次的得分即为抽出的数与相邻两个数的乘积。直到只剩下首尾两个数为止。问最小得分是多少?
归类:动态规划
该问题可以转化成矩阵连乘问题(当然也可以不转化),状态转移方程为:dp[i][j] = min(dp[i][k] + dp[k][j] + x[i] * x[k] * x[j])i + 1 <= k <= j - 1,小小的解释下状态转移方程的意思,dp[i][j]表示把第i个数字到第j个数字之间(不包括i,j)的数字去光后得到的最小值,假设k是i和j之间最后取出的那张卡片。dp[0][n-1]就是要求的值。
下面是代码:
#include <iostream>
using namespace std;
#define max 105
int dp[max][max];
int a[max];
int min(int a,int b)
{
return a>b?b:a;
}
int main()
{
int n;
cin>>n;
int i,j,k;
for(i=0;i<n;i++)
cin>>a[i];
for(i=0;i<n-2;i++)
dp[i][i+2]=a[i]*a[i+1]*a[i+2];
int len;
for(len=3;len<n;len++)
for(i=0;i+len<n;i++)
{
j=i+len;
for(k=i+1;k<j;k++)
{
if(dp[i][j]==0)
dp[i][j]=dp[i][k]+dp[k][j]+a[i]*a[k]*a[j];
else
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j]+a[i]*a[j]*a[k]);
}
}
cout<<dp[0][n-1]<<endl;
return 0;
}
另外我还参考了一个代码:
#include <stdio.h>
#include <iostream>
#include <string.h>
using namespace std;
int n;
int a[101];
int dp[101][101];
int MIN(int x,int y)
{
if(x>y)
return y;
return x;
}
int main()
{
int i,j,len,k;
while(scanf("%d",&n)!=EOF)
{
memset(dp,0,sizeof(dp));
for( i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
for( len=2;len<n;len++)
{
for( i=1;i<=n-len;i++)
{
j=i+len;
if(len==2)
dp[i][j]=a[i]*a[i+2]*a[i+1];
else
for( k=i+1;k<j;k++)
{
if(dp[i][j]==0)
dp[i][j]=dp[i][k]+dp[k][j]+a[i]*a[k]*a[j];
else
dp[i][j]=MIN(dp[i][j],dp[i][k]+dp[k][j]+a[i]*a[j]*a[k]);
}
}
}
printf("%d\n",dp[1][n]);
}
}
我的代码是对参考代码的修改,修改后思路更加清晰。
在这里我对我的代码稍微解释下:
举个例子2,4,6,8,10
for(i=0;i<n-2;i++)
dp[i][i+2]=a[i]*a[i+1]*a[i+2];//这两句是算2,4,6;;;4,6,8;;;6,8,10这是最基本的情况,即从三个中删除一个的大小,这为后面的运算做准备
下面的更加重要:
for(len=3;len<n;len++)//len表示长度,什么长度呢?还记得前面计算的2,4,6吗,长度是a[2](大小为6)数组下标是2,a[0](大小为2,下标为0),此时长度为2,这下知道长度是什么了吧。长度为3时,则为2,4,6,8
for(i=0;i+len<n;i++)
{
j=i+len;
for(k=i+1;k<j;k++)
{
if(dp[i][j]==0)
dp[i][j]=dp[i][k]+dp[k][j]+a[i]*a[k]*a[j];
else
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j]+a[i]*a[j]*a[k]);//这里是重点,k是分节点,前面是一部分,后面是一部分,如2,4,6,8其中要计算(2,4,6)(6,8)两段相应的这两段前面应该已经计算了
}
}
还是感觉说不清啊,现在知道为什么很多人只贴代码而不写思路了,原来是这样啊
浙公网安备 33010602011771号