版权声明:本文为博主原创文章,转载请注明出处哦~ https://blog.csdn.net/a54665sdgf/article/details/82226957
题意:给你一堆石子,每次只能归并连续的p-q个石子,归并的代价为归并的石子总数,求归并全部石子所需的最小代价,若无法归并则输出0。
解法:设dp[L][R][k]为将下标为L-R区间内的石子分成k堆所需的最小代价,则状态转移方程为:
考虑递归边界:
(1)R-L+1=k,此时石子堆数即为目标堆数,代价为0。
(2)p<=R-L+1<=q,k=1,此时将所有石子合并即可,代价为L~R的石子总数。
(3)L-R+1<p,此时无法合并石子,代价为INF(无穷)。
打训练赛的时候想到了,但是当时从k=1推回k=p~q的时候懵了,没有勇气写下去。快结束的时候我决定试一下,然后因为bug没有A出来。当时应该把所有的dp值输出一下看看结果,debug能力还需加强。
赛后改对的代码:
#define FRER() freopen("i.txt","r",stdin)
#include<bits/stdc++.h>
using namespace std;
const int N=100+10;
const int INF=0x3f3f3f3f;
int n,p,q,d[N][N][N],vis[N][N][N],a[N],sum[N];
int dp(int L,int R,int k)
{
if(R-L+1==k)return 0;
if(R-L+1<=q&&R-L+1>=p&&k==1)return sum[R]-sum[L-1];
if(R-L+1<p)return INF;
if(vis[L][R][k])return d[L][R][k];
if(k==1)
{
for(int i=p; i<=q; ++i)
for(int M=L; M<R; ++M)
d[L][R][k]=min(d[L][R][k],dp(L,M,1)+dp(M+1,R,i-1)-sum[L-1]+sum[R]);
}
else
{
for(int M=L; M<R; ++M)
d[L][R][k]=min(d[L][R][k],dp(L,M,1)+dp(M+1,R,k-1));
}
vis[L][R][k]=1;
return d[L][R][k];
}
int main()
{
//freopen("i.txt","r",stdin);
while(scanf("%d%d%d",&n,&p,&q)==3)
{
for(int i=0; i<=n; ++i)
for(int j=0; j<=n; ++j)
for(int k=0; k<=q; ++k)
{
d[i][j][k]=INF;
vis[i][j][k]=0;
}
for(int i=1; i<=n; ++i)scanf("%d",&a[i]);
sum[0]=0;
for(int i=1; i<=n; ++i)sum[i]=sum[i-1]+a[i];
printf("%d\n",dp(1,n,1)==INF?0:dp(1,n,1));
}
return 0;
}
//