zoukankan      html  css  js  c++  java
  • 以石子合并为例的区间DP

    区间DP,是一类具有较为固定解法的DP,一般的思路都是:

    first.初始化区间长度为1的情况(一般区间长度为1的较易于初始化)

    second.

    for(枚举区间长度2~n){
        for(枚举左端点){
         j=i+len-1//记录右端点
           for(枚举断点){
             //枚举断点后一般是比较以哪个断点分开最优(一般是比较最大或最小)
            }
        }
    }

    end.区间DP的特点:

    合并:即将两个或多个部分进行整合,当然也可以反过来,也就是对一个问题分解成两个或多个部分。

    特征:能将问题分解为两两合并的形式;

    求解:对整个问题设最优值,枚举合并点,将问题分解成左右两部分,最后合并左右两个部分的最优值得到原问题的最优值。(显然无后效性啦)

    然后我发现区间DP的数据范围都超级小,100左右


    下面我萌以石子合并为例子来看具体的看区间DP:

    石子合并【题目链接】

    首先要说的是:这道题贪心是不对哒;

    然后看正解:区间DP

    如果第i堆石子与第j堆石子合并成一堆,说明i~j之间的所有石子也都被合并成了一堆,然后对于合并第i堆石子与第j堆石子的代价,可以看做是先将第i~k堆石子合并,再将第k+1~j堆石子合并的代价别忘记再加上Σ(k=i~j)val[k](val[i]表示第i堆石子的个数,显然不管你合并啥,只要合并第i堆石子~第j堆石子,一定需要加上第i堆石子到第j堆石子的和(感性理解一下qwq)),然后需要求最优的话,就是枚举i~j之间每一个断点k,取最优。

    定义:sum[i]表示Σ(k=1~i)val[k](运用了前缀和的思想,如果要求区间i~j的和,可以用sum[j]-sum[i-1])

    f_min[i][j]表示合并区间i~j所需要的最小花费;

    f_max[i][j]表示合并区间i~j所需要的最大花费;

    初始条件:f_min[i][i]=f_max[i][i]=0;(只合并自己一堆显然需要花费为0)

    转移方程:

    f_min[i][j]=min(f_min[i][j],f_min[i][k]+f_min[k+1][j])+sum[j]-sum[i-1];

    f_max[i][j]=max(f_max[i][j],f_max[i][k]+f_max[k+1][j])+sum[j]-sum[i-1];

    (i<=k<=j)

    显然这个题需要枚举区间长度一层for,枚举区间起点一层for,枚举断点k一层for,然后就是三层for,时间复杂度O(n^3);


    然后是对于环的处理,最好写好用的处理方法,把序列延长为原来的两倍。然后最后枚举一下取最优值就好了。

    CODE:

    #include<bits/stdc++.h>
    #define ll long long
    #define INF 2147483647
    
    using namespace std;
    
    inline int read(){
        int ans=0;
        char last=' ',ch=getchar();
        while(ch<'0'||ch>'9') last=ch,ch=getchar();
        while(ch>='0'&&ch<='9') ans=(ans<<1)+(ans<<3)+ch-'0',ch=getchar();
        if(last=='-') ans=-ans;
        return ans;
    }
    
    int n,val[202];
    int f_max[202][202],f_min[202][202],sum[202];
    
    int main(){
        n=read();
        for(int i=1;i<=n;i++)
          val[i]=read(),val[i+n]=val[i];
        memset(f_min,0x3f,sizeof(f_min));
        for(int i=1;i<=2*n;i++){
            sum[i]=sum[i-1]+val[i];
            f_min[i][i]=0;f_max[i][i]=0;
        }
        for(int len=2;len<=n;len++){
            for(int i=1;i+len-1<=2*n;i++){
                int j=i+len-1;
                for(int k=i;k<=j;k++){
                    f_min[i][j]=min(f_min[i][j],f_min[i][k]+f_min[k+1][j]);
                    f_max[i][j]=max(f_max[i][j],f_max[i][k]+f_max[k+1][j]);
                }
                f_min[i][j]+=(sum[j]-sum[i-1]);
                f_max[i][j]+=(sum[j]-sum[i-1]);
            }
        }
        int ans_min=INF,ans_max=0;
        for(int i=1;i<=n;i++){
            ans_min=min(ans_min,f_min[i][i+n-1]);
            ans_max=max(ans_max,f_max[i][i+n-1]);
        }
        cout<<ans_min<<endl<<ans_max<<endl;
        return 0;
    }

    跑去做题(逃

    end-

  • 相关阅读:
    使用ssh公钥实现ssh免密码登录
    如何定义领域模型(概念模型)
    17.python字符编码检测——chardet
    21.python对象的浅拷贝和深拷贝
    15.序列化python对象
    18.python的打包和发布
    16.python的网络编程
    13.python的文件操作
    linux下python、django框架的配置
    14.python的xml操作
  • 原文地址:https://www.cnblogs.com/zhuier-xquan/p/11112216.html
Copyright © 2011-2022 走看看