• hihocoder1636 Pangu and Stones(区间DP(石子合并变形))


    题目链接:http://hihocoder.com/problemset/problem/1636

    题目大意:有n堆石头,每次只能合并l~r堆,每次合并的花费是要合并的石子的重量,问你合并n堆石子的最小花费,若不能合并则输出0。

    解题思路:

    这算是石子合并的加强版了吧,原来石子合并是只能两堆两堆地合并,而现在对合并堆数加了一个范围[l,r]。这题我看到的时候也没什么想法,看了题解才会的,而且也看的不是特别懂。

    首先定义数组dp[i][j][k]表示将[i,j]的石子合并成k堆需要的最小花费。

    那么我们可以得到状态转移方程:

    ①p>1时,dp[i][j][p]=min(dp[i][j][p],dp[i][k][p-1]+dp[k+1][j][1]),(i=<k<j,2=<p<=r)

    ②p==1时,dp[i][j][1]=min(dp[i][j][1],dp[i][j][k]+sum[j]-sum[i-1]),(l=<k<=r)

    之前还有几个疑问:

    1、为什么①中,p范围是[2,r]而不是[l,r]?

    答:[l,r]是的限制是给p==1时即合并的时候用的,单纯地划分区间并没有这个限制。

    2、为什么①的状态转移方程是可行的,难道不应该写成dp[i][j][p]=min(dp[i][j][p],dp[i][k][p-x]+dp[k+1][j][x])吗?

    答:如果手动模拟一下会发现,上面连个状态转移方程是等效的,所以①可以求出最优解,而且考虑到时间复杂度的问题,①省去了枚举x的花费,显然更优。

    代码:

     1 #include<cstdio>
     2 #include<cmath>
     3 #include<cctype>
     4 #include<cstring>
     5 #include<iostream>
     6 #include<algorithm>
     7 #include<vector>
     8 #include<queue>
     9 #include<set>
    10 #include<map>
    11 #include<stack>
    12 #include<string>
    13 #define lc(a) (a<<1)
    14 #define rc(a) (a<<1|1)
    15 #define MID(a,b) ((a+b)>>1)
    16 #define fin(name)  freopen(name,"r",stdin)
    17 #define fout(name) freopen(name,"w",stdout)
    18 #define clr(arr,val) memset(arr,val,sizeof(arr))
    19 #define _for(i,start,end) for(int i=start;i<=end;i++)
    20 #define FAST_IO ios::sync_with_stdio(false);cin.tie(0);
    21 using namespace std;
    22 typedef long long LL;
    23 const int N=2e2+5;
    24 const int INF=0x3f3f3f3f;
    25 const double eps=1e-10;
    26 
    27 int sum[N];
    28 int dp[N][N][N];
    29 
    30 int main(){
    31     int n,l,r;
    32     while(cin>>n>>l>>r){
    33         memset(dp,0x3f,sizeof(dp));
    34         for(int i=1;i<=n;i++){
    35             cin>>sum[i];
    36             sum[i]+=sum[i-1];
    37         }
    38         for(int i=1;i<=n;i++){
    39             for(int j=i;j<=n;j++){
    40                 dp[i][j][j-i+1]=0;
    41             }
    42         }
    43         for(int len=1;len<n;len++){
    44             for(int i=1;i+len<=n;i++){
    45                 int j=i+len;
    46                 //注意p要从2开始,而不是从l开始
    47                 for(int p=2;p<=r;p++){
    48                     for(int k=i;k<j;k++){
    49                         if(k-i+1<p-1) continue;
    50                         dp[i][j][p]=min(dp[i][j][p],dp[i][k][p-1]+dp[k+1][j][1]);
    51                     }
    52                 }
    53                 for(int p=l;p<=r;p++){
    54                     dp[i][j][1]=min(dp[i][j][1],dp[i][j][p]+sum[j]-sum[i-1]);
    55                 }
    56             }
    57         }
    58         if(dp[1][n][1]==INF)
    59             cout<<0<<endl;
    60         else
    61             cout<<dp[1][n][1]<<endl;
    62     }
    63     return 0;
    64 }
  • 相关阅读:
    【转载】褪去华衣 裸视学习 探讨系列
    最简单的视频网站(JavaEE+FFmpeg)
    过段时间要换博客了
    计网3
    计网1
    物理层计算题
    计网4
    子网划分与CIDR
    百度=残留在墙后的垃圾
    计网2
  • 原文地址:https://www.cnblogs.com/fu3638/p/8895522.html
Copyright © 2020-2023  润新知