石子合并

本文介绍了一种使用动态规划解决经典问题的方法,并通过实例演示了如何应用平行四边形优化技术将时间复杂度从O(n^3)降低到O(n^2)。文中提供了两段C++代码示例,分别用于解决圆形石子堆合并问题及其优化版本。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这是一道经典的动态规划问题。设f[i][j]表示合并第i个到第j个石子所得的最小得分,f[i][j] = min{f[i][j], f[i][k - 1] + f[k][j] + sum[j] - sum[i - 1]} (i < k <= j),时间复杂度是O(n3).

这道题可用平行四边形优化到O(n2),不过这个优化虽然会照着写,但还不太明白……

http://acm.nankai.edu.cn/p1137.html

这道题注意是圆形,否则会WA的,数据范围小,不需要优化

///http://acm.nankai.edu.cn/p1137.html
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define LL long long
const LL INF = (LL)1<<60;
LL sum[210], f[210][210], d[210][210], num[210];
int main()
{
    //freopen("input.txt", "r", stdin);
    int n;
    LL ans1, ans2;
    while(cin >> n){
        sum[0] = 0;
        for(int i = 1; i <= n; i++){
            cin>>num[i];
            sum[i] = sum[i - 1] + num[i];
        }
        for(int i = 1; i < n; i++){
            num[i + n] = num[i];
            sum[i + n] = sum[i + n - 1] + num[i + n];
        }
        memset(f, 0, sizeof(f));
        memset(d, 0, sizeof(d));
        for(int l = 2; l <= n; l++){
            for(int i = 1, j; i + l - 1 < 2 * n; i++){
                j = i + l - 1;
                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] + sum[j] - sum[i - 1]);
                    d[i][j] = max(d[i][j], d[i][k] + d[k + 1][j] + sum[j] - sum[i - 1]);
                }
            }
        }
        ans1 = INF;
        ans2 = 0;
        for(int i = 1; i <= n; i++){
            ans1 = min(ans1, f[i][i + n - 1]);
            ans2 = max(ans2, d[i][i + n - 1]);
        }
        cout<<ans1<<endl<<ans2<<endl;
    }
    return 0;
}
http://coder.buct.edu.cn/oj/Problem.aspx?pid=1725
用到了平行四边形优化

//四边形优化 http://coder.buct.edu.cn/oj/Problem.aspx?pid=1725
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define LL long long
const LL INF = (LL)1<<60;
const int MAXN = 1010;
LL sum[MAXN], f[MAXN][MAXN], prt[MAXN][MAXN], num[MAXN];
int main()
{
    //freopen("input.txt", "r", stdin);
    int n;
    while(cin >> n){
        sum[0] = 0;
        for(int i = 1; i <= n; i++){
            cin>>num[i];
            sum[i] = sum[i - 1] + num[i];
        }
        memset(f, 0, sizeof(f));
        memset(prt, 0, sizeof(prt));
        for(int i = 1; i <= n; i++) prt[i][i] = i;
        for(int l = 2; l <= n; l++){
            for(int i = 1, j; i <= n - l + 1; i++){
                j = i + l - 1;
                f[i][j] = INF;
                for(int k = prt[i][j - 1]; k <= prt[i + 1][j]; k++){
                    if(f[i][j] > f[i][k - 1] + f[k][j]){
                        f[i][j] = f[i][k - 1] + f[k][j];
                        prt[i][j] = k;
                    }
                }
                f[i][j] += sum[j] - sum[i - 1];
            }
        }
        cout<<f[1][n]<<endl;
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值