题目链接:https://hihocoder.com/problemset/problem/1636
题目大意:n个石子堆排成一排,每次可以将连续的最少L堆,最多R堆石子合并在一起,消耗的代价为要合并的石子总数求合并成1堆的最小代价,如果无法做到输出0
Sample Input
3 2 2 1 2 3 3 2 3 1 2 3 4 3 3 1 2 3 4
Sample Output
9 6 0
emmm,和传统的合并石子有点不太一样,关键是多了个限制,那么我们就在传统的dp状态上再加上一维堆数的限制就好了也就是$dp[i][j][k]$其表示将$[i,j]$合并为$k$堆所需要的最小代价,那么最终状态就是$dp[1][n][1]$了,而状态的转移也很容易写出来:$dp[l][r][k]=min(dp[l][r][k],dp[l][mid][k-1]+dp[mid+1][r][1])$,而需要合并的操作也就只有k=1的时候,那么也就是说这个时候我们需要将$sum[r]-sum[l-1]$加上去。那么它的方程也就是$dp[l][r][1]=min(dp[l][r][1],dp[l][mid][p]+dp[mid+1][r][1]+sum[r]-sum[l-1])$
以下是AC代码:
#include <cstdio> #include <cstring> #include <algorithm> #include <cmath> using namespace std; const int mac=110; const int inf=1e8+10; int dp[mac][mac][mac],sum[mac]; int main() { int n,L,R,x; while (scanf ("%d%d%d",&n,&L,&R)!=EOF){ memset(dp,0x3f,sizeof dp); for (int i=1; i<=n; i++) scanf ("%d",&x),sum[i]=sum[i-1]+x,dp[i][i][1]=0; for (int len=2; len<=n; len++){ for (int l=1; l+len-1<=n; l++){ int r=l+len-1; for (int k=2; k<=min(len,R); k++){//当前合成的堆数,即每次选择和并的堆数+1 for (int mid=l+k-2; mid<r; mid++){//合并了k堆的起点 dp[l][r][k]=min(dp[l][r][k],dp[l][mid][k-1]+dp[mid+1][r][1]); } } for (int p=L-1; p<=R-1; p++){ for (int mid=l+p-1; mid<r; mid++){//合并了p+1堆的起点 dp[l][r][1]=min(dp[l][r][1],dp[l][mid][p]+dp[mid+1][r][1]+sum[r]-sum[l-1]); } } } } printf("%d ",dp[1][n][1]==dp[0][0][0]?0:dp[1][n][1]); } return 0; }