其他分享
首页 > 其他分享> > [Acwing] 282. 石子合并 区间DP

[Acwing] 282. 石子合并 区间DP

作者:互联网

前言

传送门 : 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−1​f[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
**/

标签:方案,min,int,合并,cin,len,282,DP,Acwing
来源: https://blog.csdn.net/qq_34364611/article/details/120624275