石子合并(区间DP-加西亚-瓦克斯(GarsiaWachs)算法.)

题目描述

P1775 石子合并(弱化版) - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

可解决 n <= 250的数据量

#include
using namespace std;
const int maxn = 2510;
int n,N,m,M;
int num[maxn];
int mincost[maxn][maxn];
int dp[maxn][maxn];
int pre[maxn];
int main()
{
    cin >> N;
    for(int i = 1;i <= N;i++)
    {
        cin >> num[i];   
        pre[i] = num[i] + pre[i - 1];
    }
    for(int i = 1;i <= N;i++)
    {
        dp[i][i] = 0;
        mincost[i][i] = i;
    }
    
    for(int len = 1;len < N;len++)
    {
        for(int i = 1;i <= N - len; i++)
        {
            int j = len + i;
            dp[i][j] = 0x3f3f3f3f;
            for(int k = i;k <=j;k++)
            {
                if(dp[i][j] > dp[i][k] + dp[k + 1][j] + (pre[j] - pre[i - 1]))
                {
                    dp[i][j] = dp[i][k] + dp[k + 1][j] + (pre[j] - pre[i - 1]);
                    mincost[i][j] = k;
                }
            }
        }
    }
    cout << dp[1][N] << endl;
    return 0;
}

平行四边形优化: 可解决 n <= 3000的数据量

#include
using namespace std;
const int maxn = 2510;
int n,N,m,M;
int num[maxn];
int mincost[maxn][maxn];
int dp[maxn][maxn];
int pre[maxn];
int main()
{
    cin >> N;
    for(int i = 1;i <= N;i++)
    {
        cin >> num[i];   
        pre[i] = num[i] + pre[i - 1];
    }
    for(int i = 1;i <= N;i++)
    {
        dp[i][i] = 0;
        mincost[i][i] = i;
    }
    
    for(int len = 1;len < N;len++)
    {
        for(int i = 1;i <= N - len; i++)
        {
            int j = len + i;
            dp[i][j] = 0x3f3f3f3f;
            for(int k = mincost[i][j - 1];k <= mincost[i + 1][j];k++)
            {
                if(dp[i][j] >dp[i][k] + dp[k + 1][j] + (pre[j] - pre[i - 1]))
                {
                    dp[i][j] = dp[i][k] + dp[k + 1][j] + (pre[j] - pre[i - 1]);
                    mincost[i][j] = k;
                }
            }
        }
    }
    cout << dp[1][N] << endl;
    return 0;
}


 

GarsiaWachs)算法.

P1880 [NOI1995] 石子合并 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

专门用于解决石子问题,可解决 N <= 40000的数据量

最优树解法(哈夫曼树)

例如:

1 4 2 6
5 2 6
7 6
13
最终解 = 13 + 7 + 5 = 25

首先:每一次选择合并的第一个数满足num[i] <= num[i + 2] 对于全局而言虽然我们每一次的满足上式子的合并不一定是当前所有可选项中最小的,但是不会影响到我们最终结局。

例:

10 1 11 2 3

显然该次最小合并应该是2和3合5

但是根据上式我们先合并10 和 1得到11

序列变为

11 11 2 3

第二次合并为 2 3

反观先合并2 3

10 1 11 5

下一次最小合并为10和1

即:满足num[i] <= num[i + 2]的合并当前不一定是最优的,但对于全局最终结果而言是最优的。

(仅限于合并到最后一个数的情况)

#include
using namespace std;
using i64 = int64_t;
i64 ans;
int n,m;
vectornum;
int merge()
{
    int k = num.size() - 2;
    for(int i = 0;i < k;i++)
        if(num[i] <= num[i + 2])
        {
            k = i;break;
        }
    int T = num[k] + num[k + 1];
    num.erase(num.begin() + k);
    num.erase(num.begin() + k);
    //找到从k往前第一个大于T的数,插入到他的后面
    int in = -1;
     for(int i = k - 1; i >= 0;i--) //从右往左找第一个 
    {
        if(num[i]>T)
        {
            in=i;
            break;
        }
    }
    num.insert(num.begin() + in + 1,T);//因为是在后面插入,所以要+1
    return T;
}
int main()
{
    cin.tie(0) -> sync_with_stdio(false);
    cin >> n;
    for(int i = 1;i <= n;i++)
    {
        cin >> m;
        num.emplace_back(m);
    }
    for(int i = 1;i <= n - 1;i++)
         ans += merge();
    cout << ans << endl;
    return 0;
}

为什么要插入到第一个大于T的数?

由于每一轮都是从右到左找num[i] <= num[i + 2],如果插到原位k,会导致原先k - 2本不应满足条件而进行了合并,会导致最终结果不一致

你可能感兴趣的:(基础算法学习,洛谷题目集,算法,c++,开发语言)