返回顶部

区间DP

区间DP

一.石子合并1

题目描述:

有N堆石子排成一排(n<=100),现要将石子有次序地合并成一堆,规定每次只能选相邻的两堆合并成一堆,并将新的一堆的石子数,记为改次合并的得分,编一程序,由文件读入堆数n及每堆石子数(<=200);

(1)选择一种合并石子的方案,使得做n-1次合并,得分的总和最少

(2)选择一种合并石子的方案,使得做n-1次合并,得分的总和最多

输入格式:

第一行为石子堆数n 第二行为每堆石子数,每两个数之间用一空格分隔。

输出格式:

从第1行为得分最小第二行是得分最大。

该问题大致有两种,一种是链状的区间DP,另一种是环状的区间DP,处理环状的问题时,直接把链接在他自己后面即可,此处只展示链状问题的解决。

该题f[i][j]表示从第i个到第j个石子合并的最大值,用j选择断开位置以转化为子问题的最优解,这也是决策的部分。

那么就可以列出状态转移方程: f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+cost)

最大值同理:f[i][j]=max(f[i][k]+f[k+1][j]+cost)

完整代码如下:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=300;
int v[N],w[N],f[N][N],s[N],g[N][N];
int n,m,i,k;
int main(){
    cin>>n;
    for(i=1;i<=n;i++){
        scanf("%d",&s[i]);
        s[i]+=s[i-1];
    }
    memset(f,0x7f,sizeof(f));
    for(i=1;i<=n;i++)f[i][i]=0;
    for(int len=2;len<=n;len++){
        for(i=1;i+len-1<=n;i++){
            int j=i+len-1;
            for(k=i;k<j;k++){
                f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+s[j]-s[i-1]);
                g[i][j]=max(g[i][j],g[i][k]+g[k+1][j]+s[j]-s[i-1]);
            }
        }
    }
    cout<<f[1][n]<<endl<<g[1][n];
    return 0;
}
这道题还要注意最外层循环区间长度(从小到大),要先解决小的子问题,再递推解决大问题。

posted @ 2024-02-17 17:26  无敌の暗黑魔王  阅读(11)  评论(0)    收藏  举报