题意:有n个数字,每一次只能合并最少l个最多r个连续的数字,合并的代价为他们的和,询问最小代价把他们合并成一堆,如果不能合并成一堆输出0。
总结:比赛的时候只是想到了一点点区间dp,但是dp的含义没有完全想明白,导致转移方程写不出来,然后卡到了最后,后来看了一下题解dp的含义顿悟了一点点,区间dp接触的还是太少了。
题解:区间dp。
我们规定 d p [ i ] [ i + d ] [ k ] dp[i][i + d][k] dp[i][i+d][k]为合并 [ i , i + d ] [i, i + d] [i,i+d]区间为 k k k所需要的最小花费。然后得到下列转移方程。
k为1的时候需要预处理,因为后面的所有状态都是由合并成一堆的情况转移过去的,在合并为1堆的时候需要满足堆数在 [ l , r ] [l, r] [l,r]区间内,后面的转移无需再次考虑。
a c c o d e : ac\ code: ac code:
#include
using namespace std;
typedef long long ll;
#define met(a, b) memset(a, b, sizeof(a))
#define rep(i, a, b) for(int i = a; i <= b; i++)
#define per(i, a, b) for(int i = a; i >= b; i--)
#define fi first
#define se second
const int maxn = 1e3 + 10;
const int inf = 0x3f3f3f3f;
const double eps = 1e-8;
const int mod = 100003;
inline int read() {
int x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '0') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
return f * x;
}
int n, l, r, a[maxn];
ll sum[102][102];
ll dp[102][102][102];
int main() {
while(~scanf("%d%d%d", &n, &l, &r)) {
rep(i, 0, 101) rep(j, 0, 101) rep(k, 0, 101) dp[i][j][k] = inf;
met(sum, 0);
rep(i, 1, n) a[i] = read();
rep(i, 1, n) {
rep(j, i, n) {
sum[i][j] = sum[i][j - 1] + a[j];
dp[i][j][j - i + 1] = 0;
}
}
for(int d = 1; d <= n; d++) {
for(int i = 1; i + d <= n; i++) {
for(int j = i; j < i + d; j++) {
for(int k = l - 1; k <= r - 1; k++) {
dp[i][i + d][1] = min(dp[i][i + d][1], dp[i][j][k] + dp[j + 1][i + d][1] + sum[i][i + d]);
}
}
for(int k = 1; k <= n; k++) {
for(int j = i; j < i + d; j++) {
dp[i][i + d][k] = min(dp[i][i + d][k], dp[i][j][k - 1] + dp[j + 1][i + d][1]);
}
}
}
}
if(dp[1][n][1] == (ll)inf) puts("0");
else printf("%lld\n", dp[1][n][1]);
}
return 0;
}