282. 石子合并

状态转移方程:
\(f(i, j) = min\{s[j] - s[l - 1] + f[i][k] + f[k + 1][j]\} (k = i, i + 1, ..., r - 1)\)

第一种(循环区间长度和左端点)

#include<iostream>
using namespace std;

const int N = 310, INF = 0x3f3f3f3f;
int f[N][N];
int a[N], s[N];
int n;

int main(){
    cin >> n;
    for(int i = 1; i <= n; i ++) cin >> a[i];
    for(int i = 1; i <= n; i ++) s[i] = a[i] + s[i - 1];
    
    /*
        1. 合并一堆石子的代价为0 <=> f[i][i] = 0
        2. len必须从2开始否则得到的矩阵会是[INF]nxn
        3. 使用枚举两个区间端点的时候需要考虑保证之前的状态被计算过
    */
    
    for(int len = 2; len <= n; len ++)
        for(int l = 1; l + len - 1 <= n; l ++){
            int r = l + len - 1;
            f[l][r] = INF;
            for(int k = l; k < r; k ++)
               f[l][r] = min(f[l][r], f[l][k] + f[k + 1][r] + s[r] - s[l - 1]);
        }
        
    cout << f[1][n];
    
    return 0;
}

第二种(循环两个区间端点)

#include<iostream>
using namespace std;

const int N = 310, INF = 0x3f3f3f3f;
int f[N][N];
int a[N], s[N];
int n;

int main(){
    cin >> n;
    for(int i = 1; i <= n; i ++) cin >> a[i];
    for(int i = 1; i <= n; i ++) s[i] = a[i] + s[i - 1];
    
    
    /*
        k + 1必定大于i,而此时f[k + 1][j]还没有被计算,
        这就是为什么循环i要倒着来的原因了。
    */
    
    for(int i = n; i >= 1; i --)
        for(int j = i + 1; j <= n; j ++){
            f[i][j] = INF;
            for(int k = i; k < j; k ++)
                f[i][j] = min(f[i][j], f[i][k] + f[k + 1][j] + s[j] - s[i - 1]);
        }
        
    cout << f[1][n];
    
    return 0;
}
posted @ 2020-09-11 15:50  yys_c  阅读(146)  评论(0编辑  收藏  举报