题意
:有
N
堆石子,每次能够合并连续的、大于等于
L
、小于等于
R
堆石子,代价是这些石子的个数和。问合并成一堆石子的代价最小值。
使用一个
dp
[l][r][k]
记录
将区间
[
l,r
]
的石子合并为
k
堆需要的最小代价。用一个
d[
i
]
记录第
k
堆石子有几个石子
转移方程:
dp
(
l,r,k
)=min{
dp
(l,i,1)+
dp
(i+1,r,k-1)},l<=
i
<k
dp
(l,r,1)=min{
dp
(
l,r,x
)}+(d[l]+…+d[r]),L<=x<=R
//当时在现场赛的时候没推出来转移方程,胡乱分析了四个小时,甚至队员想出了dfs离散化之类的骚操作...然并卵还是没做出来然后打铁回家
#include <bits/stdc++.h>
using namespace std;
const int maxn=120,inf=1000000;
int dp[maxn][maxn][maxn],d[maxn],mi,ma;
int n;
int init()
{
for(int i=1;i<=n;i++)
{
cin>>d[i];
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
for(int k=1;k<=n;k++)
{
dp[i][j][k]=-1;
}
for(int i=1;i<=n;i++)
{
dp[i][i][1]=d[i];
}
}
int solve(int l,int r,int k)
{
if(dp[l][r][k]==-2)return -2;
if(l==r&&dp[l][r][k]!=-1)return 0;
if(dp[l][r][k]!=-1)return dp[l][r][k];
if(k==1)
{int m=inf;
if(l!=r+1)
{
for(int i=mi;i<=ma;i++)
{
int cur=solve(l,r,i);
if(m>cur&&cur>=0){m=cur;}
}
}
else m=0;
if(m!=inf)
for(int i=l;i<=r;i++)
{
m+=d[i];
}
else m=-2;
dp[l][r][1]=m;
return m;
}
int m=inf;
for(int i=l;i<r;i++)
{
int cur1=solve(l,i,1),cur2=solve(i+1,r,k-1);
if(cur1==-2||cur2==-2)continue;//{dp[l][r][k]=-2;return -2;}
if(m>cur1+cur2){ if(l==i)cur1=0;if(i+1==r)cur2=0; m=cur1+cur2;}
}
if(m>=inf){dp[l][r][k]=-2;return -2;}
else dp[l][r][k]=m;
return m;
}
int main()
{
while( cin>>n>>mi>>ma)
{init();
int cc=solve(1,n,1);
if(cc<0)cc=0;
cout<<cc<<endl;
}
return 0;
}