zoukankan      html  css  js  c++  java
  • 动态规划:区间DP与环形DP

    区间型动态规划的典型例题是石子归并,同时使用记忆化搜索实现区间动归是一种比较容易实现的方式,避免了循环数组实现的时候一些边界的判断

    n堆石子排列成一条线,我们可以将相邻的两堆石子进行合并,合并之后需要消耗的代价为这两堆石子的质量之和,问最小的合并代价

    状态转移方程很容易给出:

    f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+sum[i][j])

    因为要计算区间和,考虑前缀和进行预处理

    然后我们给出用记忆化搜索形式实现的代码,这里的记忆化搜索形式可以作为后续问题的一个模板

     1 #include<iostream>
     2 #include<cstring>
     3 #include<algorithm>
     4 using namespace std;
     5 const int INF=0x3f3f3f3f;
     6 const int maxn=105;
     7 int n;
     8 int w[maxn];
     9 int g[maxn];  //前缀和 
    10 int f[maxn][maxn];
    11 int dfs(int l,int r)
    12 {
    13     if(l==r) return 0;
    14     if(f[l][r]!=INF)
    15         return f[l][r];
    16     int tmp=INF;
    17     for(int i=l;i<r;i++)
    18         tmp=min(tmp,dfs(l,i)+dfs(i+1,r)+g[r]-g[l-1]);
    19     if(tmp<f[l][r])
    20     f[l][r]=tmp;
    21     return f[l][r];
    22 }
    23 int main()
    24 {
    25     cin>>n;
    26     for(int i=1;i<=n;i++)
    27     {
    28         cin>>w[i];
    29         g[i]=g[i-1]+w[i];
    30     }
    31     for(int i=1;i<=n;i++)
    32     for(int j=1;j<=n;j++)
    33         f[i][j]=0x3f3f3f3f;
    34     cout<<dfs(1,n)<<endl;
    35     return 0;
    36 }

    这个问题还是比较显然的,我们考虑另一个问题,那就是环形动态规划

    其实环形动态规划也是区间型,只不过区间首尾相接

    此时使用记忆化搜索实现,其实是不容易的

    典型例题是能量项链

    先给出状态转移方程:

    f[i][j]=max(f[i][j],f[i][k]+f[k][j]+a[i]*a[j]*a[k])

    由于每一种区间问题的价值计算方式不一样,可能采用不同的优化形式,本题直接计算即可

    然后我们给出使用循环数组方式实现的一个固定的格式,所有的区间型动态规划都可以采取这样的形式来实现

     1 #include<iostream>
     2 #include<cstring>
     3 using namespace std;
     4 const int maxn=1005;
     5 int n;
     6 int a[maxn];
     7 long long ans=0;
     8 int f[maxn][maxn];
     9 void dp()
    10 {
    11     for(int l=2;l<=n;l++)  //区间长度 
    12     for(int i=1;i<=n*2-l+1;i++)  //区间起点 
    13     {
    14         int j=i+l;  //区间终点 
    15         for(int k=i+1;k<=j-1;k++)  //区间中任意点 
    16             f[i][j]=max(f[i][j],f[i][k]+f[k][j]+a[i]*a[j]*a[k]);
    17     }
    18     for(int i=1;i<=n;i++)
    19     if(ans<f[i][i+n])
    20         ans=f[i][i+n];
    21 }
    22 int main()
    23 {
    24     cin>>n;
    25     for(int i=1;i<=n;i++)
    26     {
    27         cin>>a[i];
    28         a[n+i]=a[i];
    29     }
    30     dp();
    31     cout<<ans;
    32     return 0;
    33 }

    分别枚举区间的长度,区间的起点和区间中的任意点就好了

  • 相关阅读:
    cordova的安装与配置
    JavaScript-string
    JavaScript-Number
    android-适配器
    网络操作-转码(乱码情况处理)
    网络操作-请求优先级
    Android读取权限
    I/O-<File区别>
    I/O-<File实例>
    I/O-<文件读写、输出>
  • 原文地址:https://www.cnblogs.com/aininot260/p/9308791.html
Copyright © 2011-2022 走看看