HihoCoder

题目链接: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;
}
路漫漫兮
原文地址:https://www.cnblogs.com/lonely-wind-/p/13264434.html