首页 > 其他分享 >决策单调性优化DP 学习笔记 & P4767 [IOI2000] 邮局 题解

决策单调性优化DP 学习笔记 & P4767 [IOI2000] 邮局 题解

时间:2023-07-17 22:35:37浏览次数:45  
标签:le P4767 IOI2000 题解 sum mid int 村庄 邮局

0. 题面

题目描述

高速公路旁边有一些村庄。高速公路表示为整数轴,每个村庄的位置用单个整数坐标标识。没有两个在同样地方的村庄。两个位置之间的距离是其整数坐标差的绝对值。

邮局将建在一些,但不一定是所有的村庄中。为了建立邮局,应选择他们建造的位置,使每个村庄与其最近的邮局之间的距离总和最小。

你要编写一个程序,已知村庄的位置和邮局的数量,计算每个村庄和最近的邮局之间所有距离的最小可能的总和。

输入格式

第一行包含两个整数:第一个是村庄 \(V\) 的数量,第二个是邮局的数量 \(P\)。

第二行包含 \(V\) 个整数。这些整数是村庄的位置。

输出格式

第一行包含一个整数\(S\),它是每个村庄与其最近的邮局之间的所有距离的总和。

样例 #1

样例输入 #1

10 5 
1 2 3 6 7 9 11 22 44 50

样例输出 #1

9

提示

对于 \(40\%\) 的数据,\(V \leq 300\)。

对于 \(100\%\) 的数据,\(1 \leq P \leq 300\),\(P \leq V \leq 3000\),$1 \leq $ 村庄位置 \(\leq 10000\)。

1. 朴素算法

容易看出这是一道 DP 题。不妨令 \(a_i\) 为位置第 \(i\) 大的村庄的位置(约定\(a_0=-\infty\),\(a_{V+1}=\infty\)),\(f_{i,j}\) 为修建 \(i\) 个邮局且最后一个邮局在 \(a_j\) 处时前 \(j\) 个村庄与其最近的邮局之间的所有距离的总和,\(w(l,r)\) 为在 \(a_l\) 和 \(a_r\) 处修建邮局后在 \(a_l\) 和 \(a_r\) 之间的所有村庄与其最近的邮局之间的所有距离的总和。不难得出以下递推式:

\[f_{i,j}=\begin{cases} \min_{k=1}^{j-1} \left\{ f_{i-1,k}+w(k,j) \right\} & 2 \le i \le P \\ w(0,j) & i = 1 \end{cases} \]

答案即为 $$ans=\min_{j=1}^{V}{f_{p,j}+w(j,V+1)} $$

事实上,使用前缀和技术,\(w\) 函数可以在 \(\Theta(1)\) 的时间复杂度内计算。约定 \(mid=\lfloor{\dfrac{a_l+a_r}{2}}\rfloor\),\(sum_i\) 为在位置 \(i\) 及其左侧所有村庄的位置值之和,\(cnt_i\) 为在位置 \(i\) 及其左侧所有村庄的数量,可以知道

\[\begin{aligned} w(l,r) &= \sum_{i=l}^{r} \min\{a_i-a_l\ , a_r-a_i\ \} \\ &= \sum_{i \ge l \ \land\ a_i \le mid}(a_i-a_l) + \sum_{i \le r \ \land\ a_i > mid}(a_r-a_i) \qquad //在\ mid\ 左侧的村庄离\ a_l 更近,在\ mid\ 右侧的村庄离\ a_r 更近 \\ &= \sum_{i \ge l \ \land\ a_i \le mid}a_i - \sum_{i \ge l \ \land\ a_i \le mid}a_l + \sum_{i \le r \ \land\ a_i > mid}a_r - \sum_{i \le r \ \land\ a_i > mid}a_i \\ &= (sum_{mid}-sum_{a_l-1})-a_l(cnt_{mid}-cnt_{a_l-1}) +a_r(cnt_{a_r}-cnt_{mid}) - (sum_{a_r}-sum_{mid}) \end{aligned} \]

于是我们得到了一个时间复杂度为 \(\Theta(VP^2)\) 的算法。但是它太慢了,需要优化。

2. 决策单调性优化

设 \(f_{i,j}=f_{i-1,p_{i,j}}+w(k,p_{i,j})\) ,我们称 \(p_{i,j}\) 为 \(f_{i,j}\) 的“最优决策点”。可以知道,递推式 \(f\) 具有“决策单调性”,即对于每个 \(i,j\),均有 \(p_{i,j} \le p_{i,j+1}\)。证明如下:

\[先放着,以后再证\\ 反正大概就是证明“四边形不等式”,即 w(a,c)+w(b,d)\le w(a,d)+w(b,c)\ (a\le b\le c\le d) ,\\ 进而证明 f具有单调性,即f_{i,j}\le f_{i,j+1}\\ (其实从f的意义上也能看出,因为两个邮局离得越远距离和就越大) \]

因此可以发现,如果求得 \(p_i\),则可以知道 \(p_j\le p_i(j<i)\),\(p_j\ge p_i(j>i)\) ,这样我们枚举法求 \(p\) 时就可以排除许多无用状态。因此可以想到一个分治算法,代码如下:

void dfs(int l,int r,int ll,int rr){
    //l,r: f下标范围
    //ll,rr: p可能的范围
    if(l>r)return;
    int mid=(l+r)/2,p=0;
    f[mid]=INF;
    for(int i=ll;i<mid;i++){
        if(f[mid]>g[i]+w(a[i],a[mid])){
            f[mid]=g[i]+w(a[i],a[mid]);
            p=i;
        }
    }//枚举求
    dfs(l,mid-1,ll,p);
    dfs(mid+1,r,p,rr);
}

不难发现这个算法的时间复杂度是 \(\Theta(VP)\) ,足以通过此题。完整代码如下:

#include <iostream>
#include <algorithm>
using namespace std;
const int MAXP=300,MAXV=3000,MAXX=10000,INF=0x3f3f3f3f;
int v,p,a[MAXV+5],cnt[MAXX+5],sum[MAXX+5],pre[MAXX+5],suf[MAXX+5],R,f[MAXV+5],g[MAXV+5];
int w(int l,int r){
    int mid1=(l+r)/2;
    return sum[mid1]-sum[l-1]-l*(cnt[mid1]-cnt[l-1])+r*(cnt[r]-cnt[mid1])-(sum[r]-sum[mid1]);
}
void dfs(int l,int r,int ll,int rr){
    if(l>r)return;
    int mid=(l+r)/2,k=0;
    f[mid]=INF;
    for(int i=ll;i<mid;i++){
        if(f[mid]>g[i]+w(a[i],a[mid])){
            f[mid]=g[i]+w(a[i],a[mid]);
            k=i;
        }
    }
    dfs(l,mid-1,ll,k);
    dfs(mid+1,r,k,rr);
}
int main(){
    ios::sync_with_stdio(false);
    cin>>v>>p;
    for(int i=1;i<=v;i++){
        cin>>a[i];
        cnt[a[i]]++;
        sum[a[i]]+=a[i];
    }
    sort(a+1,a+1+v);
    R=a[v];
    for(int i=1;i<=R;i++){
        cnt[i]+=cnt[i-1];
        sum[i]+=sum[i-1];
    }
    for(int i=1;i<=R;i++){
        pre[i]=i*cnt[i]-sum[i];
        suf[i]=sum[R]-sum[i-1]-i*(cnt[R]-cnt[i-1]);
    }
    for(int i=1;i<=v;i++){
        f[i]=pre[a[i]];
    }
    int ans=INF;
    for(int i=2;i<=p;i++){
        swap(f,g);
        dfs(1,v,1,v);
    }
    for(int i=1;i<=v;i++){
        ans=min(ans,f[i]+suf[a[i]]);
    }
    cout<<ans<<endl;
    return 0;
}

标签:le,P4767,IOI2000,题解,sum,mid,int,村庄,邮局
From: https://www.cnblogs.com/ztxcsl/p/17561457.html

相关文章

  • 题解 P7215
    点分治。考虑当前的分治重心的城市被完全联通。这可以用队列接解决。每次放入一种城市,就把那些城镇的父亲加入队列,如果存在城镇不在当前分治重心的联通块内,那么说明必定存在另一个分治重心能算到它,直接退出即可。剩下的和模板没有任何区别。复杂度\(O(n\logn)\)。code:#in......
  • 题解 P6329
    点分树模板题。是个神奇的算法。点分树就是将分治重心按照层级连边,每个节点父亲的联通块都包含了当前节点的联通块,且高度为\(\logn\)。可以解决不考虑树的形态的多次询问带修改的问题。对于这道题,我们可以在点分树的每个节点上记录两棵树状数组,分别与当前节点距离为\(k\)的......
  • 题解 P3345
    点分树。本题的核心问题在于不好直接确定补给站的位置。但是仔细思考后我们发现,对于当前节点,如果存在一个儿子的答案比它优,那么补给站一定在那个儿子的子树中。增量为\(w\times(sum_u-2\cdotsum_v)\)。当\(v\)优于\(u\)时,\(2\cdotsum_v>sum_u\)。如果\(v_2\)也满足,则......
  • 题解 P5384
    这题有紫??对于询问节点\(u\),倍增地跳到它的\(k\)级祖先,求其子树内有多少深度为\(dep_u\)的节点。我们考虑把询问离线,再通过dfs序把树拍平,然后扫一遍直接求就行了。复杂度\(O(n\logn)\)。code:#include<bits/stdc++.h>usingnamespacestd;constintN=1e6+5;inlin......
  • 题解 P3806
    点分治模板题。点分治适合处理大规模的树上路径信息问题暴力做法:dfs每个点\(u\),算出其子树内每个点到\(u\)的距离,统计经过\(u\)的所有路径,复杂度\(O(n^2)\)。容易发现,复杂度和子树大小有关。对于当前子树,我们可以求出其重心,计算经过重心的所有路径,删掉重心,递归每个联通......
  • 题解 CF1271D
    贪心+DP。对于一个点,后选显然比先选好,也就是说每个点都对应了唯一一个来源。于是我们可以把每个点所能回溯到的点的收益值从大到小排序,贪心地选前缀。定义\(f_{i,j}\)表示考虑了前\(i\)个点,剩下\(j\)个人,最大收益。转移方程和\(01\)背包的一样。\[f_{i,j}=f_{i-1,j-b......
  • 题解 CF213C
    考虑朴素DP。定义\(f_{i,j,i2,j2}\)表示两个人分别在\((i,j),(i2,j2)\)时获得的最大收益。复杂度\(O(n^4)\),不行。我们换种方法,定义\(f_{st,x,y}\)表示两人同时走了\(st\)步,分别向右走了\(x,y\)步。显然如果向右的步数确定了,向下的也确定了。转移方程如下:\[f_{st,x......
  • 题解 CF1265E
    期望DP。定义\(f_i\)表示第\(i\)个镜子照成功的期望天数,\(p_i\)为第\(i\)天成功的概率,\(q_i\)为第\(i\)天失败的概率。根据题意容易列出方程:\[f_i=(f_{i-1}+1)\cdotp_i+(f_{i-1}+1+f_i)\cdotq_i\]移项得:\[(1-q_i)\cdotf_i=(f_{i-1}+1)\cdot(p_i+q_i)\]同除以......
  • 题解 CF930C
    好题啊好题。定义\(a_i\)为有多少个区间包含\(i\)。拍脑袋一想,当且仅当存在顺序的三个坐标\((i,j,k)\)满足\(a_i>a_j\)且\(a_j<a_l\)时,可以确定没有数被所有区间包含。这个结论很简单,因为如果存在,则\(a\)序列必定为一个“山峰”。而如果出现上面这种情况,说明有“山......
  • 题解 P6772 [NOI2020] 美食家
    观察数据范围,\(T\)很大,\(n\)很小,用矩乘。对于一条边\((u,v,w)\),我们将\(u\)拆成\(w-1\)个点,并连接\((u_0,u_1,0),(u_1,u_2,0)...(u_{w-2},u_{w-1},0)\)和\((u_{w-1},v_0,c_{v})\),总点数\(5n\)。将美食节按时间排序,相邻两个美食节之间用矩阵快速幂转移,然后再加上附加......