区间dp。。。好难的说
这题目咋一看根本想不到是区间dp,我是做区间dp专题时看到的这题,但是就算知道是用区间dp做也划分不好状态,后来瞄了一眼状态划分,顿时被这题的思路惊呆了,将栈的特性应用的淋漓尽致,让人叹服。
题目分析:和普通的区间dp不同,这题无论任何情况下,都设当前区间最前面的一个元素为分割点,应用了栈的特性:若第一个元素第k个出栈,则第二到k个元素肯定在第一个元素之前出栈,第k+1到最后一个元素肯定在第k个之后出栈,这样便把区间划分成了两段,以此状态依次划分,加上记忆化搜索,便能得出答案。这题的记忆化搜索也和一般区间dp不同,区间内的值会随着区间之前共取出多少元素的改变而改变,故对要记忆的区间,记录下共有多少元素在此区间之前出栈,然后下次用到该区间是和记录的出栈元素个数比较,去掉多加或多减的部分,便能完成记忆化搜索。
#include <iostream>
#include <cstring>
using namespace std;
int dp[101][101];
int q[101];
int INF=0x3f3f3f3f;
int mark[101][101];
int sum[101];
int min(int a,int b)
{
return a>b?b:a;
}
int interval(int l,int r,int k)
{
if (l>r)
return 0;
if (mark[l][r]>=0)
{
int a=(k-mark[l][r])*(sum[r]-sum[l-1])+dp[l][r];
return a;
}
dp[l][r]=INF;
for (int i=1;i<=r-l+1;i++)
{
int p=interval(l+1,l+i-1,k)+interval(l+i,r,k+i)+(i+k-1)*q[l];
dp[l][r]=min(dp[l][r],p);
}
mark[l][r]=k;
return dp[l][r];
}
int main()
{
int T;
cin>>T;
for (int r=1;r<=T;r++)
{
int n;
cin>>n;
sum[0]=0;
for (int i=1;i<=n;i++)
{
scanf("%d",&q[i]);
sum[i]=sum[i-1]+q[i];
}
memset (dp,0,sizeof(dp));
memset(mark,-1,sizeof(mark));
interval(1,n,0);
printf("Case #%d: %d\n",r,dp[1][n]);
}
}