51Nod 1022 石子归并 V2(区间DP+四边形优化)

题目链接:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1022

题目大意:

N堆石子摆成一个环。现要将石子有次序地合并成一堆。规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的代价。计算将N堆石子合并成一堆的最小代价。

例如: 1 2 3 4,有不少合并方法

1 2 3 4 => 3 3 4(3) => 6 4(9) => 10(19)

1 2 3 4 => 1 5 4(5) => 1 9(14) => 10(24)

1 2 3 4 => 1 2 7(7) => 3 7(10) => 10(20)

括号里面为总代价可以看出,第一种方法的代价最低,现在给出n堆石子的数量,计算最小合并代价。

解题思路:经典的石子合并问题,较原来不同的是石子是环形摆放的,而且石子数目n的范围有100增加到了1000,原本O(n^3)的算法肯定是会超时的。所以需要用四边形不等式优化将复杂度降为O(n^2),并且将数组倍增把环变为链。

粗略介绍一下四边形优化的作用,具体证明看这里《动态规划加速原理之四边形不等式》。

我们原本的状态转移方程为dp[i][j]=min{dp[i][k]+dp[k+1][j]+w[i][j]}(i<j,i<=k<=j)。

上式在动态规划的状态转移方程中是很常见的,对于上式中的w(i,j)
如果符合w(i`,j) <= w(i,j`) i<i`<j<j`        那么我们称函数w满足关于区间包含的单调性。
如果符合w(i,j)+w(i`,j`) <= w(i`,j)+w(i,j`)    那么我们称函数w满足四边形不等式。

那么就可以使用两个定理(图片来源

于是,我们可以使用s[i][j]记录使得dp[i][j]最优的分割点(k点),并且满足s[i][j-1]<=s[i][j]<=s[i+1][j+1],那么我们的k的枚举范围就是s[i][j-1]<=s[i][j]<=s[i+1][j+1]。

复杂度证明:

代码:

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #include<algorithm>
 5 using namespace std;
 6 const int N=2e3+5;
 7 const int INF=0x3f3f3f3f;
 8
 9 int dp[N][N],s[N][N],sum[N],a[N];//s[i][j]为使dp[i][j]最优的分割点
10
11 int main(){
12     int n;
13     scanf("%d",&n);
14     memset(dp,0x3f,sizeof(dp));
15     for(int i=1;i<=n;i++){
16         scanf("%d",&a[i]);
17         a[i+n]=a[i];
18     }
19     for(int i=1;i<=2*n;i++){
20         dp[i][i]=0;
21         sum[i]=sum[i-1]+a[i];
22         s[i][i]=i;
23     }
24     for(int d=1;d<n;d++){
25         for(int i=1;i<=2*n-d;i++){
26             int j=i+d;
27             for(int k=s[i][j-1];k<=s[i+1][j];k++){
28                 int tmp=dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1];
29                 if(tmp<dp[i][j]){
30                     dp[i][j]=tmp;
31                     s[i][j]=k;
32                 }
33             }
34         }
35     }
36     int ans=INF,d=n-1;
37     for(int i=1;i<=2*n-d;i++){
38         int j=i+d;
39         ans=min(ans,dp[i][j]);
40     }
41     printf("%d\n",ans);
42     return 0;
43 }

 

时间: 2024-08-24 17:51:15

51Nod 1022 石子归并 V2(区间DP+四边形优化)的相关文章

[51nod 1022] 石子归并v2 [dp+四边形不等式优化]

题面: 传送门 思路: 加强版的石子归并,现在朴素的区间dp无法解决问题了 首先我们破环成链,复制一条一样的链并粘贴到原来的链后面,变成一个2n长度的序列,在它上面dp,效率O(8n^3) 显然是过不了的,需要优化 注意:dp的转移如下:dp[i][j]=min(dp[i][k]+dp[k+1][j]+sum(i,j)),其中sum(i,j)表示i到j的价值和,满足区间单调性 因此dp[i][j]也满足区间单调性,可以用四边形不等式优化 我们令s[i][j]等于让dp[i][j]取最小值的那个K

51Nod - 1021 石子归并(区间DP)

[题目描述] N堆石子摆成一条线.现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的代价.计算将N堆石子合并成一堆的最小代价. 例如: 1 2 3 4,有不少合并方法 1 2 3 4 => 3 3 4(3) => 6 4(9) => 10(19) 1 2 3 4 => 1 5 4(5) => 1 9(14) => 10(24) 1 2 3 4 => 1 2 7(7) => 3 7(10) => 1

51nod 1022 石子归并 V2(四边形不等式)

分析:记dp[i][j]为从i到j合并的最小代价(顺时针,i可以大于j),sum[i][j]为从i到j的和,则dp[i][j]=min{dp[i][k-1]+dp[k][j]}+sum[i][j],(i<k<=j),直接求的话复杂度为O(n^3),会T. 四边形不等式优化:记s[i][j]为dp[i][j]取得最小值时对应的k值,则有dp[i][j]=min{dp[i][k-1]+dp[k][j]}+sum[i][j],(s[i][j-1]<=k<=s[i+1][j]),可以证明,

HDU 3506 (环形石子合并)区间dp+四边形优化

Monkey Party Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 131072/65536 K (Java/Others)Total Submission(s): 1699    Accepted Submission(s): 769 Problem Description Far away from our world, there is a banana forest. And many lovely monkeys l

石子归并(codevs_1048)——区间dp

很经典的一道区间dp题. 突然觉得数据那么小,好像可以随便乱搞. #include<iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; inline int read(){ int t=1,num=0;char c=getchar(); while(c>'9'||c<'0'){if(c=='-')t=-1;c=getchar()

POJ 1160 (区间DP+四边形优化)

这个转移方程不好想,尤其是一段值的解是中间,不明觉厉.dp[i][j] 用i个邮局,覆盖前j个村庄的最小值. 还有就是区间dp的平行四边形优化,这个题的转移方程并不是"区间DP",所以枚举状态要逆着(很花时间),且用一个邮局覆盖都是从0断开了相当于没有断开. 类比于石子归并,矩阵链乘等标准区间DP,其所需状态之前就已经获得,不用倒推 #include <cstdio> #include <cstring> #include <iostream> us

codevs——1048 石子归并 (区间DP)

时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题解 题目描述 Description 有n堆石子排成一列,每堆石子有一个重量w[i], 每次合并可以合并相邻的两堆石子,一次合并的代价为两堆石子的重量和w[i]+w[i+1].问安排怎样的合并顺序,能够使得总合并代价达到最小. 输入描述 Input Description 第一行一个整数n(n<=100) 第二行n个整数w1,w2...wn  (wi <= 100) 输出描述 Output Descriptio

石子归并(区间DP)

-->测评传送门 题目描述 有n堆石子排成一列,每堆石子有一个重量w[i], 每次合并可以合并相邻的两堆石子,一次合并的代价为两堆石子的重量和w[i]+w[i+1].问安排怎样的合并顺序,能够使得总合并代价达到最小. 输入描述第一行一个整数n(n<=100) 第二行n个整数w1,w2...wn (wi <= 100) 输出描述 一个整数表示最小合并代价 样例输入 4 4 1 1 4 样例输出 18 当时第一次学DP的一个坎儿,现在DP重现,终于弄会了QWQ 解析: 第一次学长讲的时候说要

hdu3516 Tree Construction (区间dp+四边形优化)

构造方法肯定是把相邻两个点连到一起,变成一个新点,然后再把新点和别的点连到一起.... 设f[i,j]为把第i到j个点都连到一起的代价,那么答案就是f[1,n] f[i,j]=min{f[i,k]+f[k+1,j]+x[k+1]-x[i]+y[k]-y[j]} (画一画就知道了) 然后显然满足四边形不等式(怎么就显然了??) 1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include&l