前言
传送门 : https://www.acwing.com/problem/content/description/284/
感觉 和 线性dp差不多 只是考虑状态转移的 时候不一样了
(怎么不一样说不上来)
思路
要求 : 所有合并方案中 使得 答案最小的方案
状态表示 : f[i][j] 表示从第 i 堆 合并到 第 j 堆的 最小方案
(为什么呢? 考感觉吧 QAQ)
状态转移 :
第 i 堆 合并到 第 j 堆 一定可以表示为
第 i 堆 先合并到 第 k堆 然后 第k+1堆 合并到 第 j堆
即 :
f
[
i
]
[
j
]
=
f
[
i
]
[
k
]
+
f
[
k
+
1
]
[
j
]
f[i][j]= f[i][k]+f[k+1][j]
f[i][j]=f[i][k]+f[k+1][j]
因为每次合并 都有 值的相加 但是又不能改变原来的
所以我们还需要多处理一个 前缀和 数组
f [ i ] [ j ] = min i ≤ k ≤ j − 1 f [ i ] [ k ] + f [ k + 1 ] [ j ] + s [ j ] − s [ i − 1 ] f[i][j]=\min _{i \leq k \leq j-1} f[i][k]+f[k+1][j]+s[j] - s[i-1] f[i][j]=mini≤k≤j−1f[i][k]+f[k+1][j]+s[j]−s[i−1]
因此本题就好做了
(哈哈哈哈哈 好难啊)
CODE
/**
所有合并方式的 最小方案
方案是 (n-1)!
f[i][j]表示 将i到j合并成一堆的方案的集合
状态计算
i<j 时
f[i][j] = min(f[i][k]+f[k+1][j]+s[j]-s[i-1])
i = j
f[i][j] = 0
**/
#include <bits/stdc++.h>
using namespace std;
const int N = 310;
int a[N],s[N],f[N][N];
void solve()
{
int n;
cin>>n;
for(int i=1; i<=n; i++)
{
cin>>a[i];
s[i] +=s[i-1]+a[i];
}
for (int len = 1; len < n; len ++) // len表示i和j堆下标的差值
{
for (int i = 1; i + len <= n; i ++)
{
int j = i + len; // 自动得到右端点
f[i][j] = 1e8;
for (int k = i; k <= j - 1; k ++) // 必须满足k + 1 <= j
{
f[i][j] = min(f[i][j], f[i][k] + f[k + 1][j] + s[j] - s[i - 1]);
}
}
}
cout<<f[1][n]<<endl;
}
int main()
{
ios::sync_with_stdio(false);
solve();
return 0;
}
/**
当前第i次操作 合并j和j-1堆 最小的花费
f[i][j][j-1]
f[0][1~n][1~n] 0
f[1][]
失败的想法
**/
/**
cin>>n;
for(int i =1;i<=n;i++)
cin>>a[i];
for(int i =1;i<=n;i++)
{
for(int j=2;j<=n;j++)
{
f[i][j][j-1]
}
}
for(int i=2;i<=n-1;i++)
{
}
失败的尝试 无法确定 i-1 和 i+1
**/