邮局加强版:四边形不等式优化DP

题目描述

一些村庄建在一条笔直的高速公路边上,我们用一条坐标轴来描述这条公路,每个村庄的坐标都是整数,没有两个村庄的坐标相同。两个村庄的距离定义为坐标之差的绝对值。我们需要在某些村庄建立邮局。使每个村庄使用与它距离最近的邮局,建立邮局的原则是:所有村庄到各自使用的邮局的距离总和最小。数据规模:1<=村庄数<=1600, 1<=邮局数<=200, 1<=村庄坐标<=maxlongint

输入

2行第一行:n m {表示有n个村庄,建立m个邮局} 第二行:a1 a2 a3 .. an {表示n个村庄的坐标}

输出

1行第一行:l 个整数{表示最小距离总和}

样例输入

10 5

1 2 3 6 7 9 11 22 44 50

样例输出

9

这道题目是IOI2000的真题哦~

可以这样考虑:

给定一个区间,假设我们要建一个邮局,那么一定是在这个序列的中点,所以我们可以先预处理出序列区间[i,j]之间的距离

一个邮局的最短距离记录为sum[i][j],然后用f[i][j]表示到i个村庄建立j个邮局的最短距离和,那么就有状态转移方程:

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

这样,代码就好写了。

但是——这个数据,用O(n3) 的普通DP算法显然无法通过。

O(n3)代码如下:

#include<bits/stdc++.h>

using namespace std;

int n,m;

int a[1666];

long long sum[1666]1666];

long long f[1666][222];

//f[i][j]表示前i个村庄设j个邮局

//sum[i][j]表示在第i个村庄到第j个村庄设一个邮局的路程

int main(void){

    cin>>n>>m;

    for (int i=1;i<=n;i++) cin>>a[i];

    sort(a+1,a+n+1);

    for (int i=1;i<=n;i++){

        for (int j=i;j<=n;j++){

            sum[i][j]=dis(i,j);

        }

    }

    memset(f,0x3f,sizeof(f));

    for (int i=1;i<=n;i++){

        f[i][1]=sum[1][i];

    }

    for (int i=1;i<=n;i++){

        for (int j=2;j<=min(i,m);j++){

            for (int k=j-1;k<=i-1;k++){

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

            }

        }

    }

    cout<<f[n][m]<<endl;

}

这东西肯定过不了啊~

那怎么办?"四边形不等式!"

f[a][c]+f[b][d]<=f[b][c]+f[a][d]

( a < b <= c< d )

(可以理解成:交叉小于包含)

满足这个条件的DP方程(或者说是别的什么数组啊之类的)就称为***为凸。

(以下一段文字来自https://blog.csdn.net/noiau/article/details/72514812

给出两个定理:

1、如果上述的w函数同时满足区间包含单调性和四边形不等式性质,那么函数dp也满足四边形不等式性质

我们再定义s(i,j)表示 dp(i,j) 取得最优值时对应的下标(即 i≤k≤j 时,k 处的 dp 值最大,则 s(i,j)=k此时有如下定理

2、假如dp(i,j)满足四边形不等式,那么s(i,j)单调,即 s(i,j)≤s(i,j+1)≤s(i+1,j+1)

大家可以自己尝试推倒一下,为什么f[i][j]和sum[i][j]是满足这个式子的(因为我懒得再推了)

再就是要证明"决策单调"

(以下一段文字来自https://blog.csdn.net/noiau/article/details/72514812

如果我们用s[i][j]表示dp[i][j]取得最优解的时候k的位置的话 
那么我们要证明如下结论的成立性: 
s[i][j-1]<=s[i][j]<=s[i+1][j] 
对于s[i][j-1]<=s[i][j]来说,我们先令dp[i][j-1]取得最优解的时候的k值为y,然后令除了最优值以外的其他值可以为x,这里我们由于要讨论单调性,所以让x小于y,即x<=y<=j-1< j

这里的证明更为繁琐,在实际应用中,我们可以写出O(n3)后,自己跑一边是否决策单调,不是就输出"false"就行了。

在这道题中,我们要注意三点:

  1. s数组(决策数组)的初始化
  2. 循环的次序
  3. 对邮局多于村庄的特判(血泪)

话不多说,代码上

#include<bits/stdc++.h>

using namespace std;

int n,m;

int a[1606];

long long sum[1606][1606];

long long f[1606][202];

int s[1606][202];

//s是决策数组

int main(void){

    cin>>n>>m;

    if(m>=n){

printf("0");

return 0;

}

    for (int i=1;i<=n;i++) cin>>a[i];

    sort(a+1,a+n+1);

    for (int i=1;i<=n;i++){

        sum[i][i]=0;

        for (int j=i+1;j<=n;j++){

            sum[i][j]=sum[i][j-1]+a[j]-a[(i+j)/2];

        }

    }

    memset(f,0x3f,sizeof(f));

//注意这里f要初始化成最大值

    memset(s,0,sizeof(s));

    for (int i=1;i<=n;i++){

        f[i][1]=sum[1][i];

        s[i][1]=0;

    }

    for (int j=2;j<=m;j++){

        s[n+1][j]=n;

        for (int i=n;i>=j;i--){

            for (int k=s[i][j-1];k<=s[i+1][j];k++){

                if (f[k][j-1]+sum[k+1][i]<f[i][j]){

                    f[i][j]=f[k][j-1]+sum[k+1][i];

                    s[i][j]=k;

                }

            }

        }

    }

    cout<<f[n][m]<<endl;

}

这样的代码,经过四边形不等式的优化,就是O(n2)的算法了!

(以下一段文字来自https://blog.csdn.net/noiau/article/details/72514812

关于O(n^2)复杂度的证明

其实证明很简单,对于一个i,j来说,我们要for s[i][j-1]到s[i+1][j]个数,那么所有的i和j加起来一共会for多少次呢?

我们可以这样思考

(s[2][2]-[1][1])+(s[3][3]-s[2][2])+(s[4][4]-s[3][3])+…+(s[n][n]-s[n-1][n-1])=s[n][n]-s[1][1]很显然是小于n的嘛,所以本来是(n *n *n)的复杂度,就这样降成了O(n *n)啦

(关于四边形不等式强推https://blog.csdn.net/noiau/article/details/72514812这篇博客)

FFFeiya编辑于2018.7.30

原文地址:https://www.cnblogs.com/FFFeiya/p/9392342.html

时间: 2024-10-07 20:45:40

邮局加强版:四边形不等式优化DP的相关文章

石子合并(四边形不等式优化dp)

该来的总是要来的———————— 经典问题,石子合并. 对于 f[i][j]= min{f[i][k]+f[k+1][j]+w[i][j]} From 黑书 凸四边形不等式:w[a][c]+w[b][d]<=w[b][c]+w[a][d](a<b<c<d) 区间包含关系单调: w[b][c]<=w[a][d](a<b<c<d) 定理1:  如果w同时满足四边形不等式和决策单调性 ,则f也满足四边形不等式 定理2:  若f满足四边形不等式,则决策s满足 s[i

BZOJ 1010 玩具装箱toy(四边形不等式优化DP)(HNOI 2008)

Description P教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京.他使用自己的压缩器进行压缩,其可以将任意物品变成一堆,再放到一种特殊的一维容器中.P教授有编号为1...N的N件玩具,第i件玩具经过压缩后变成一维长度为Ci.为了方便整理,P教授要求在一个一维容器中的玩具编号是连续的.同时如果一个一维容器中有多个玩具,那么两件玩具之间要加入一个单位长度的填充物,形式地说如果将第i件玩具到第j个玩具放到一个容器中,那么容器的长度将为 x=j-i+Sigma(Ck) i<

四边形不等式优化dp

对四边形不等式优化dp的理解 四边形不等式适用于优化最小代价子母树问题,即f[i][j]=max/min(f[i][k-1]+f[k][j])+w[i][j],类似枚举中间点的dp问题,典型例题石子归并; 如果w函数满足区间包含的单调性和四边形不等式,那么函数f也满足四边形不等式,如果f满足四边形不等式,s[i][j]=max/min{t|f[i][j]=f[i][k-1]+f[k][j]}+w[i][j],也就是s[i][j]是f[i][j]取得最优解的中间点,s[i][j]具有单调性; 即s

【转】斜率优化DP和四边形不等式优化DP整理

当dp的状态转移方程dp[i]的状态i需要从前面(0~i-1)个状态找出最优子决策做转移时 我们常常需要双重循环 (一重循环跑状态 i,一重循环跑 i 的所有子状态)这样的时间复杂度是O(N^2)而 斜率优化或者四边形不等式优化后的DP 可以将时间复杂度缩减到O(N) O(N^2)可以优化到O(N) ,O(N^3)可以优化到O(N^2),依次类推 斜率优化DP和四边形不等式优化DP主要的原理就是利用斜率或者四边形不等式等数学方法 在所有要判断的子状态中迅速做出判断,所以这里的优化其实是省去了枚举

四边形不等式优化DP——石子合并问题 学习笔记

好方啊马上就要区域赛了连DP都不会QAQ 毛子青<动态规划算法的优化技巧>论文里面提到了一类问题:石子合并. n堆石子.现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分. 求出将n堆石子合并成一堆的最小得分和最大得分以及相应的合并方案. 设m[i,j]表示合并d[i..j]所得到的最小得分. 状态转移方程: 总的时间复杂度为O(n3). [优化方案] 四边形不等式: m[i,j]满足四边形不等式 令s[i,j]=max{k | m[

HDU 2829 Lawrence (斜率优化DP或四边形不等式优化DP)

题意:给定 n 个数,要你将其分成m + 1组,要求每组数必须是连续的而且要求得到的价值最小.一组数的价值定义为该组内任意两个数乘积之和,如果某组中仅有一个数,那么该组数的价值为0. 析:DP状态方程很容易想出来,dp[i][j] 表示前 j 个数分成 i 组.但是复杂度是三次方的,肯定会超时,就要对其进行优化. 有两种方式,一种是斜率对其进行优化,是一个很简单的斜率优化 dp[i][j] = min{dp[i-1][k] - w[k] + sum[k]*sum[k] - sum[k]*sum[

POJ 1160 四边形不等式优化DP Post Office

d(i, j)表示用i个邮局覆盖前j个村庄所需的最小花费 则有状态转移方程:d(i, j) = min{ d(i-1, k) + w(k+1, j) } 其中w(i, j)的值是可以预处理出来的. 下面是四边形不等式优化的代码: 1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <cstring> 5 using namespace std; 6 7 con

POJ 1160 Post Office (四边形不等式优化DP)

题意: 给出m个村庄及其距离,给出n个邮局,要求怎么建n个邮局使代价最小. 析:一般的状态方程很容易写出,dp[i][j] = min{dp[i-1][k] + w[k+1][j]},表示前 j 个村庄用 k 个邮局距离最小,w可以先预处理出来O(n^2),但是这个方程很明显是O(n^3),但是因为是POJ,应该能暴过去..= =,正解应该是对DP进行优化,很容易看出来,w是满足四边形不等式的,也可以推出来 s 是单调的,可以进行优化. 代码如下: #pragma comment(linker,

[HDU2829] Lawrence [四边形不等式优化dp]

题面: 传送门 思路: 依然是一道很明显的区间dp 我们设$dp\left[i\right]\left[j\right]$表示前$j$个节点分成了$i$块的最小花费,$w\left[i\right]\left[j\right]$表示把闭区间$\left[i,j\right]$放在一起产生的价值 那么转移就比较明显了: $dp\left[i\right]\left[j\right]=min\left(dp\left[i-1\right]\left[k-1\right]+w\left[k\right