区间dp
ICPC Beijing 2017 J, Pangu and Stones
题目链接:http://oj.daimayuan.top/course/8/problem/327
题目大意:有n堆石子,每对有ai个,每次可以合并[L, R]堆石子,代价是这些石子的之和
f[l][r][k]:表示将l,r合并成k堆的最小代价
划分方式:根据最后一顿划分的分界线在哪里
f[l][r][1]:表示合并成一堆的代价,我们利用前面的计算的f[l][r][k]的k是否在L-R之间在进行真正的代价合并
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 110;
const LL inf = 1ll<<60;
int a[N], s[N];
LL f[N][N][N];
int main(){
int n, L, R;
while(scanf("%d %d %d", &n , &L, &R) == 3){
for(int i = 1; i <= n; i ++) scanf("%d", &a[i]);
for(int i = 1; i <= n; i ++) s[i] = s[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 ++){
f[i][j][k] = inf;
}
}
}
for(int len = 1; len <= n; len ++){
for(int l = 1; l + len - 1 <= n; l ++){
int r = l + len - 1;
// 边界,一般边界写好了,下面按照思路正常进行dp就可以了
if(len == 1) f[l][r][1] = 0;
else{
// 合并其实并没有发生在枚举mid那里,mid哪里只是找把l,r变成k堆的最小代价
// 而是k这个循环的最后,才真正的将l,r合并起来
for(int k = 2; k <= n; k ++){
for(int mid = l; mid < r; mid ++){
f[l][r][k] = min(f[l][r][k], f[l][mid][1] + f[mid+1][r][k-1]);
}
if(k >= L && k <= R) f[l][r][1] = min(f[l][r][1], f[l][r][k] + s[r] - s[l-1] );
}
}
}
}
if(f[1][n][1] <= (1ll<<60)/2) printf("%lld\n", f[1][n][1]);
else puts("0");
}
return 0;
}
ICPC Kunming 2020 C, Cities
题目链接:http://oj.daimayuan.top/course/8/problem/327
题目大意:给定一个长度为n(<=5000)的序列,每次可以选择一段连续相等的序列把它变成另外一个数,问使该序列变成相同的最少操作次数,每个数出现的次数不超过15次
f[l][r]表示将l,r合并成相同的最小代价的话
那么我们需要枚举和a[l]相等的其他位置,如果这个位置是r的话,显然我们可以用f[l+1][r]来更新f[l][r]
如果相等的位置是在中间的话,我们可以发现一个性质,一定存在一种替换方案,使得al...ar变成al并且次数是最小的,所以现在的结果直接用f[l][p]+f[p][r]更新就可以了,我们把他们都染成a[l]这个数字
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 5010, inf = 1<< 29;
vector<int> pos[N];
int f[N][N];
int a[N];
int main(){
int T;
cin >> T;
while(T--){
int n;
cin >> n;
for(int i = 1; i <= n; i++){
scanf("%d", &a[i]);
pos[a[i]].clear();
}
for(int i = 1; i <= n; i++){
pos[a[i]].push_back(i);
}
for(int len = 1; len <= n; len ++){
for(int l = 1; l + len - 1 <= n; l ++){
int r = l + len - 1;
f[l][r] = inf;
}
}
for(int len = 1; len <= n; len ++){
for(int l = 1; l + len - 1 <= n; l ++){
int r = l + len - 1;
if(len == 1) f[l][r] = 0;
else{
f[l][r] = f[l+1][r] + 1;
for(auto p : pos[a[l]]){
if(p > r) break;
if(p <= l) continue;
if(p == r) f[l][r] = min(f[l][r], f[l+1][r]);
else f[l][r] = min(f[l][r], f[l+1][p] + f[p][r] );
}
}
}
}
printf("%d\n", f[1][n]);
{
}
return 0;
}