合并石子(洛谷)

本文探讨了一种使用C语言解决合并石子问题的方法,通过动态规划策略来寻找最优解。博客详细解释了算法思路和代码实现过程。

合并石子

#include<iostream>  
#include<cstdio>  
#include<cmath>  
using namespace std;   
int n,minl,maxl,f1[300][300],f2[300][300],num[300];  
int s[300];  
inline int d(int i,int j){return s[j]-s[i-1];}  
//转移方程:f[i][j] = max(f[i][k]+f[k+1][j]+d[i][j];
int main()  
{   
    scanf("%d",&n);  
    for(int i=1;i<=n+n;i++)  //好吧,终于有时间看看评论区,看来大家对这里异议蛮多的,这里统一解释一下,因为是一个环,所以需要开到两倍再枚举分界线,最后肯定是最大的 
    {  
        scanf("%d",&num[i]);  
        num[i+n]=num[i];  
        s[i]=s[i-1]+num[i];  
    }  
    for(int p=1;p<n;p++)  
    {  
        for(int i=1,j=i+p;(j<n+n) && (i<n+n);i++,j=i+p)  
        {  
            f2[i][j]=999999999;  
            for(int k=i;k<j;k++)  
            {  
                f1[i][j] = max(f1[i][j], f1[i][k]+f1[k+1][j]+d(i,j));   
                f2[i][j] = min(f2[i][j], f2[i][k]+f2[k+1][j]+d(i,j));  
            }  
        }  
    }  
    minl=999999999;  
    for(int i=1;i<=n;i++)  
    {  
        maxl=max(maxl,f1[i][i+n-1]);  
        minl=min(minl,f2[i][i+n-1]);  
    }  
    printf("%d\n%d",minl,maxl);  
    return 0;  
}
洛谷平台的数字石子合并题中,常涉及不同类型的石子合并,主要使用动态规划(dp)算法求解。对于只能相邻两堆进行合并的情况,可采用区间dp求解。 一个较大的石子堆由两个较小的相邻石子合并形成,即相邻的小区间不断合并成大区间,这符合区间DP的思想。其转移方程是将大的堆拆成两个小的石子堆,枚举中间的点,每次计算合成这两小堆已用代价加上这次合并的代价(这次合并的代价即整个区间的和),找到最小值: ```plaintext for(int k=i;k<j;k++) //k为分割点 dp[i][j] = min(dp[i][j],dp[i][k]+dp[k+1][j]+a[i] + a[i+1] +...+ a[j] ) ``` 核心代码如下: ```cpp #include <iostream> #include <cstring> using namespace std; const int N = 310; int a[N],prefix[N]; int dp[N][N]; //表示到了区间[i->j]时的最小花费 void solve() { memset(dp,0x3f,sizeof(dp)); int n; cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; for(int i=1;i<=n;i++) prefix[i]=prefix[i-1]+a[i]; //前缀和计算区间和 for(int i=1;i<=n;i++) dp[i][i]=0; //单独合并自己的初始化 //区间DP模板 for(int len=2;len<=n;len++) //枚举分割的区间长度 { for(int i=1;i+len-1<=n;i++) //枚举左端点 { int j=i+len-1; //此时右端点可计算 for(int k=i;k<j;k++) //再对(i->j)进行小区间分割计算,枚举分割点 { //遍历计算最优的分割情况,[i][k]<->[k+1][j]+合并(i->j)的所有石头的权重 dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+(prefix[j]-prefix[i-1])); } } } cout<<dp[1][n]<<'\n'; //即输出[1->n]的最小花费 } ``` 此外,对于环形石子合并问题,可使用四边形不等式优化。dp[i][j] 的最大值总是在合并端点处取得,也就是 `max(dp[i+1][j],dp[i][j−1])`。代码如下: ```cpp #include<bits/stdc++.h> #define ll long long using namespace std; int n,w[205],sum[205],dp[205][205],s[205][205]; int inf=0x3fffffff,ans=inf; int main() { cin>>n; for(int i=1;i<=n;i++) { cin>>w[i]; sum[i]=sum[i-1]+w[i]; s[i][i]=i; } for(int i=n+1;i<=2*n;i++) { w[i]=w[i-n]; s[i][i]=i; sum[i]=sum[i-1]+w[i]; } auto getvalue=[&](int l,int r) { return sum[r]-sum[l-1]; }; for(int i=1;i<=2*n;i++) { dp[i][i]=0; } for(int i=2*n-1;i>=1;i--) for(int j=i+1;j<=2*n;j++) dp[i][j]=min(dp[i+1][j],dp[i][j-1])+getvalue(i,j); for(int i=1;i<=n;i++) ans=min(ans,dp[i][i+n-1]); cout<<ans<<endl; for(int i=2*n-1;i>=1;i--) for(int j=i+1;j<=2*n;j++) dp[i][j]=max(dp[i+1][j],dp[i][j-1])+getvalue(i,j); ans=-1; for(int i=1;i<=n;i++) ans=max(ans,dp[i][i+n-1]); cout<<ans; return 0; } ```
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值