首先什么是四边形不等式呢?设w是整数定义下的一个二元函数,形式如下 设 a ≤ b ≤ c ≤ d 设a\leq b\leq c\leq d 设a≤b≤c≤d 有 w ( a , c ) + w ( b , d ) ≤ w ( a , d ) + w ( b , c ) 有w(a,c)+w(b,d)\leq w(a,d)+w(b,c) 有w(a,c)+w(b,d)≤w(a,d)+w(b,c)
在这个四边形里面, a d + b c > a c + b d ad+bc\gt ac+bd ad+bc>ac+bd显然成立,为什么?考虑 △ o a c \triangle oac △oac和 △ o b d \triangle obd △obd,根据三角形性质,显然有 o a + o c > a c oa+oc\gt ac oa+oc>ac o b + o d > b d ob+od\gt bd ob+od>bd两边分别相加有 o a + o d + o c + o b > a c + b d oa+od+oc+ob\gt ac+bd oa+od+oc+ob>ac+bd也就是 a d + b c > a c + b d ad+bc\gt ac+bd ad+bc>ac+bd,那什么时候相等,当abcd四点共线的时候相等,这也就是四边形不等式 a d + b c ≥ a c + b d ad+bc\geq ac+bd ad+bc≥ac+bd
#include<iostream>#include<algorithm>#include<cstring>#include<cstdio>#include<vector>#include<cmath>#include<queue>#include<stack>#include<map>usingnamespace std;typedeflonglong ll;constint INF =0x3f3f3f3f;constint MAXN =1e6+100;constdouble eps =1e-6;int Data[MAXN];int SUM[MAXN];int dp[2050][2050];int s[2050][2050];intmain(){
int n;while(cin >> n){
for(int i=1;i<=n;i++){
cin >> Data[i];
Data[i + n]= Data[i];}for(int i=1;i<=2*n;i++){
SUM[i]= SUM[i -1]+ Data[i];}for(int i=1;i<=2*n;i++){
dp[i][i]=0;}for(int len =1; len < n; len++){
for(int i=1;i<=2*n-len;i++){
int j=i+len;
dp[i][j]= INF;for(int k=i;k<j;k++){
dp[i][j]=min(dp[i][j], dp[i][k]+ dp[k +1][j]+ SUM[j]- SUM[i -1]);}}}int ans = INF;for(int i=1;i<=n;i++){
ans =min(ans, dp[i][i + n -1]);}
cout << ans <<"\n";}return0;}
上面的程序是区间DP问题的一般性框架,可以看到,第一圈for循环是区间长度,不可省略,第二圈for循环是区间的左端点,也不能省略,这两圈是必要的,那么唯一可以优化的就是第三圈的for,这个for是在干嘛呢,这个for循环的作用是寻找区间 [ i , j ] [i,j] [i,j]内的最优分割点,四边形优化就是在这里将 [ i , j ] [i,j] [i,j]缩小,设 d p [ i , j ] dp[i,j] dp[i,j]表示动态规划的一个状态量,定义 s [ i , j ] s[i,j] s[i,j]为 d p [ i , j ] dp[i,j] dp[i,j]取得最小值对应的 k k k值,可以证明 s [ i ] [ j − 1 ] ≤ s [ i ] [ j ] ≤ s [ i + 1 ] [ j ] s[i][j-1]\leq s[i][j]\leq s[i+1][j] s[i][j−1]≤s[i][j]≤s[i+1][j]也就是 s [ i ] [ j − 1 ] ≤ k ≤ s [ i + 1 ] [ j ] s[i][j-1]\leq k\leq s[i+1][j] s[i][j−1]≤k≤s[i+1][j]利用这个不等式,我们可以将k的枚举范围从 [ i , j ] [i,j] [i,j]缩小到 [ s [ i ] [ j − 1 ] , s [ i + 1 ] [ j ] ] [s[i][j-1],s[i+1][j]] [s[i][j−1],s[i+1][j]],优化后的程序如下
#include<iostream>#include<algorithm>#include<cstring>#include<cstdio>#include<vector>#include<cmath>#include<queue>#include<stack>#include<map>usingnamespace std;typedeflonglong ll;constint INF =0x3f3f3f3f;constint MAXN =1e6+100;constdouble eps =1e-6;int Data[MAXN];int SUM[MAXN];int dp[2050][2050];int s[2050][2050];intmain(){
int n;while(cin >> n){
for(int i=1;i<=n;i++){
cin >> Data[i];
Data[i + n]= Data[i];}for(int i=1;i<=2*n;i++){
SUM[i]= SUM[i -1]+ Data[i];}for(int i=1;i<=2*n;i++){
dp[i][i]=0;
s[i][i]= i;}for(int len =1; len < n; len++){
for(int i=1;i<=2*n-len;i++){
int j=i+len;
dp[i][j]= INF;for(int k=s[i][j-1];k<=s[i+1][j];k++){
int tmp = dp[i][k]+ dp[k +1][j]+ SUM[j]- SUM[i -1];if(dp[i][j]> tmp){
dp[i][j]= tmp;
s[i][j]= k;}}}}int ans = INF;for(int i=1;i<=n;i++){
ans =min(ans, dp[i][i + n -1]);}
cout << ans <<"\n";}return0;}
这样就可以通过这道题
原理证明
下面讨论的都是取最小值的四边形优化。下面来证明 s [ i ] [ j − 1 ] ≤ s [ i ] [ j ] ≤ s [ i + 1 ] [ j ] s[i][j-1]\leq s[i][j]\leq s[i+1][j] s[i][j−1]≤s[i][j]≤s[i+1][j]考虑右侧不等号 设 m k [ i , j ] = m [ i , k ] + m [ k , j ] m_k[i,j]=m[i,k]+m[k,j] mk[i,j]=m[i,k]+m[k,j] s [ i ] [ j ] = d s[i][j]=d s[i][j]=d如果说 d d d是最优分割,因为取得是最小值,那么应该有 m k [ i , j ] ≥ m d [ i , j ] m_k[i,j]\geq m_d[i,j] mk[i,j]≥md[i,j]现在 d d d是最优分割,那么扩展到下一个区间的时候, d d d也仍然应该是最优分割,也就是 m k [ i + 1 , j ] ≥ m d [ i + 1 , j ] m_k[i+1,j]\geq m_d[i+1,j] mk[i+1,j]≥md[i+1,j]仍然成立,那么根据数学归纳法,如果能够证明这个式子,也就能够说明 s [ i ] [ j ] ≤ s [ i + 1 ] [ j ] s[i][j]\leq s[i+1][j] s[i][j]≤s[i+1][j],注意根据数学归纳法,我们现在已知条件是 m k [ i , j ] ≥ m d [ i , j ] m_k[i,j]\geq m_d[i,j] mk[i,j]≥md[i,j],要证明的是 m k [ i + 1 , j ] ≥ m d [ i + 1 , j ] m_k[i+1,j]\geq m_d[i+1,j] mk[i+1,j]≥md[i+1,j]
将这两个式子整体作差,得到 ( m k [ i + 1 , j ] − m d [ i , j ] ) − ( m k [ i , j ] − m d [ i , j ] ) (m_k[i+1,j]-m_d[i,j])-(m_k[i,j]-m_d[i,j]) (mk[i+1,j]−md[i,j])−(mk[i,j]−md[i,j])根据 m k [ i + 1 , j ] = m [ i + 1 , k ] + m [ k , j ] m_k[i+1,j]=m[i+1,k]+m[k,j] mk[i+1,j]=m[i+1,k]+m[k,j] m k [ i , j ] = m [ i , k ] + m [ k , j ] m_k[i,j]=m[i,k]+m[k,j] mk[i,j]=m[i,k]+m[k,j] m d [ i + 1 , j ] = m [ i + 1 , d ] + m [ d , j ] m_d[i+1,j]=m[i+1,d]+m[d,j] md[i+1,j]=m[i+1,d]+m[d,j] m d [ i , j ] = m [ i , d ] + m [ d , j ] m_d[i,j]=m[i,d]+m[d,j] md[i,j]=m[i,d]+m[d,j]全部代入化简得到 m [ i + 1 , k ] − m [ i , k ] + m [ i , d ] − m [ i + 1 , d ] m[i+1,k]-m[i,k]+m[i,d]-m[i+1,d] m[i+1,k]−m[i,k]+m[i,d]−m[i+1,d]因为 i < i + 1 ≤ k ≤ d i\lt i+1\leq k\leq d i<i+1≤k≤d根据四边形不等式,有 m [ i , k ] + m [ i + 1 , d ] ≤ m [ i , d ] + m [ i + 1 , k ] m[i,k]+m[i+1,d]\leq m[i,d]+m[i+1,k] m[i,k]+m[i+1,d]≤m[i,d]+m[i+1,k]整理得到 m [ i + 1. k ] − m [ i , k ] + m [ i , d ] − m [ i + 1 , d ] ≥ 0 m[i+1.k]-m[i,k]+m[i,d]-m[i+1,d]\geq 0 m[i+1.k]−m[i,k]+m[i,d]−m[i+1,d]≥0正好对应刚才的化简结果,而这个结果正好对应着 m k [ i + 1 , j ] − m k [ i , j ] ≥ m d [ i + 1 , j ] − m d [ i , j ] m_k[i+1,j]-m_k[i,j]\geq m_d[i+1,j]-m_d[i,j] mk[i+1,j]−mk[i,j]≥md[i+1,j]−md[i,j]再移项,回头看一眼已知条件,可以得到 m k [ i + 1 , j ] − m d [ i + 1 , j ] ≥ m k [ i , j ] − m d [ i , j ] ≥ 0 m_k[i+1,j]-m_d[i+1,j]\geq m_k[i,j]-m_d[i,j]\geq 0 mk[i+1,j]−md[i+1,j]≥mk[i,j]−md[i,j]≥0也就是 m k [ i + 1 , j ] ≥ m d [ i + 1 , j ] m_k[i+1,j]\geq m_d[i+1,j] mk[i+1,j]≥md[i+1,j]
右侧 ≤ \leq ≤证明完成,左侧 ≤ \leq ≤证明方式类似,只不过改为考虑 j j j而不是 i i i
时间复杂度分析
这三层 f o r for for循环最外侧是 O ( n ) O(n) O(n)的,现在考虑内侧两层 f o r for for,这里还是以石子合并问题作为例子,但是降低难度,不要环形,改成直线,程序如下
考虑内侧两层 f o r for for,每个 k k k的循环次数是 s [ i + 1 [ j ] − s [ i ] [ j − 1 ] + 1 s[i+1[j]-s[i][j-1]+1 s[i+1[j]−s[i][j−1]+1次,那么经过 n − l e n n-len n−len次循环过后,内侧两层 f o r for for循环次数为(累加) ∑ i = 1 n − l e n s [ i + 1 ] [ j ] − s [ i ] [ j − 1 ] + 1 = s [ 2 ] [ j ] − s [ n − l e n ] [ j − 1 ] + n − l e n \sum_{i=1}^{n-len}s[i+1][j]-s[i][j-1]+1=s[2][j]-s[n-len][j-1]+n-len i=1∑n−lens[i+1][j]−s[i][j−1]+1=s[2][j]−s[n−len][j−1]+n−len = n − l e n + s [ 2 ] [ j ] − s [ n − l e n ] [ j − 1 ] =n-len+s[2][j]-s[n-len][j-1] =n−len+s[2][j]−s[n−len][j−1]n后面都是常数,所以内侧两层 f o r for for的时间复杂度是 O ( n ) O(n) O(n),所以三层 f o r for for总的时间复杂度为 O ( n 2 ) O(n^2) O(n2)