2017 ACM-ICPC北京赛区 - J - Pangu and Stones
题解:
一道让我很兴奋的题。作为一个区间DP刚入门2天的萌新可以推对这道题并且过题。
言归正传:
1,集合:l,r区间合并成k堆石头的方案的答案。
2,属性:最小值。
3,状态:l,r,k(堆)(有时候last点可能也会成为一种状态)
4,last点:我们枚举每个点作为区间划分的时候答案都是可能不同的,所以last点就是区间中任意一点。
那么根据last点和状态我们开始写转移方程:
P点是划分点。
f[l][r][k]=min(f[l][r][k],f[l][p][k-1],f[p+1][r][1])
这里为什么是k-1和k呢?我们仔细想一下,我们想想我们k-1堆的最小不可分子集是不是一堆一堆合并上去的?对吧!所以其实我们k堆通过k-1堆和另外一堆的合并来的,所以相通这个之后我们成功合并成了K堆。
那么如果我们k满足在题目中[l,r]这个区间内的话,是不是我们可以直接把这k堆合并成一堆,并且我们这k堆合并的代价是一定的!他就是区间权值和,所以我们开头需要预处理一下前缀和。这样我们就完成了k堆合并成一堆的任务了!然后直接找答案就可以了。
合成一堆的方程f[l][r][1]=min(…,f[l][r][k]+sum[r]-sum[l-1])
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e2+7;
const int inf=1e9+7;
int dp[N][N][N];
int a[N],sum[N];
int main(){
int n,l,r;
while(scanf("%d%d%d",&n,&l,&r)!=EOF){
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
sum[i]=sum[i-1]+a[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]=inf;
for(int len=1;len<=n;len++){
for(int i=1;i+len-1<=n;i++){
int j=i+len-1;
dp[i][j][len]=0;
for(int k=2;k<=len;k++){
for(int p=i;p<=j;p++)
dp[i][j][k]=min(dp[i][j][k],dp[i][p][1]+dp[p+1][j][k-1]);
if(k>=l&&k<=r)
dp[i][j][1]=min(dp[i][j][1],dp[i][j][k]+sum[j]-sum[i-1]);
}
}
}
if(dp[1][n][1]>=inf) printf("0\n");
else printf("%d\n",dp[1][n][1]);
}
}