文章目录

  • CF1579G - Minimal Coverage
    • 题目描述
    • 分析
      • 做法1:DP
      • 做法2:贪心(bitset优化+二分

CF1579G - Minimal Coverage

题目描述

  • 给出 n n n 条线段,第 i i i 条线段长度为 a i a_i ai​。
  • 现在要求将所有线段按照给出的顺序依次放置在一个无限长的数轴上
    并且满足当前放置线段的起点等于上一条放置线段的终点
  • 初始线段起点为 0 0 0
  • 现在要求所有线段的覆盖的长度(即线段的并)最短
  • t t t 组数据, 1 ≤ t ≤ 1000 1 \leq t \leq 1000 1≤t≤1000,保证 ∑ n ≤ 1 0 4 \sum n \leq 10^4 ∑n≤104
  • 对于每组数据 1 ≤ a i ≤ 1000 , 1 ≤ n ≤ 1 0 4 1 \leq a_i \leq 1000,1 \leq n \leq 10^4 1≤ai​≤1000,1≤n≤104

分析

很不错的一道题,虽然没有多难,但是题目很多做法和细节都值得思考

做法1:DP

首先可以把所有线段想象成若干次跳跃,那么可以想到以数轴长度作为状态转移的DP单次转移都是 O ( 1 ) O(1) O(1) 的。

具体看题目要求,我们要求线段的覆盖距离最短,那么需要记录一个到达过的最左距离 L L L 和最右距离 R R R ,和当前在位置 p o s pos pos,但是显然我们受数据限制没法开这么多维记录。

思考这个问题,我们首先可以把跳跃的范围缩短到 [ − m a x { a i } , m a x { a i } ] [-max \{a_i \},max \{a_i \}] [−max{ai​},max{ai​}],这里不做严格证明,并且我不知道怎么证在下述算法运行时这个结论成立。

简单的论述一下:假设我们这次跳跃的位置的绝对值第一次超过 m a x { a i } max \{a_i \} max{ai​},那么上一次的位置处在数轴相同一侧 [ 1 , m a x { a i } ) [1,max \{a_i \}) [1,max{ai​}) 的位置。那么如果我们往相反一侧跳跃,就不会超过上述范围,因此我们可以有效的把长度压缩至 [ − 1000 , 1000 ] [-1000,1000] [−1000,1000],但是显然复杂度依旧不够。

观察上图,我们DP的过程显然就是把这种关系描述出来,但是其实 p o s pos pos,或者说这一段在哪儿并不重要,我们只在乎他们之间的绝对位置。因此我们其实只需要转而将 L , p o s , R L,pos,R L,pos,R 这三个点之间的两段距离用差值DP展现出来即可。

第一眼肯定是想设 f ( i , j , k ) f(i,j,k) f(i,j,k) 代表第 i i i 条线段到最左的距离为 j j j,最右距离为 k k k 时能否达到,用 b o o l bool bool 数组表示状态。显然复杂度又不太够。。。这里就要继续用一些技巧优化。

我们发现上述状态只表示 01 01 01 似乎有点浪费,我们把状态DP的值来表示,设 f ( i , j ) f(i,j) f(i,j) 代表第 i i i 条线段的结束点距离到达过的最左位置的距离为 j j j 时离达到过的最右位置的最小距离。也就是说:
f ( i , j , k ) = { 0 / 1 } − > f ( i , j ) = m i n { k } f(i,j,k)= \{0/1 \} -> f(i,j)=min \{k \} f(i,j,k)={0/1}−>f(i,j)=min{k}

那么转移方程就很显然了,我们考虑怎么放第 i + 1 i+1 i+1 条线段,也就是 i i i 到 i + 1 i+1 i+1 的转移:

  • 第 i + 1 i+1 i+1 条线段往左拐:
    那么离左边界的距离就是 m a x ( 0 , j − a i + 1 ) max(0,j-a_{i+1}) max(0,j−ai+1​),而距离右边界增长 a i a_i ai​
    f ( i + 1 , m a x ( 0 , j − a [ i + 1 ] ) ) = m i n ( f ( i + 1 , m a x ( 0 , j − a [ i + 1 ] ) , f ( i , j ) + a [ i + 1 ] ) f(i+1,max(0,j-a[i+1]))=min(f(i+1,max(0,j-a[i+1]),f(i,j)+a[i+1]) f(i+1,max(0,j−a[i+1]))=min(f(i+1,max(0,j−a[i+1]),f(i,j)+a[i+1])

  • 第 i + 1 i+1 i+1 条线段往右拐:
    f ( i + 1 , j + a [ i + 1 ] ) = m i n ( f ( i + 1 , j + a [ i + 1 ] ) , m a x ( 0 , f ( i , j ) − a [ i + 1 ) ) f(i+1,j+a[i+1])=min(f(i+1,j+a[i+1]),max(0,f(i,j)-a[i+1)) f(i+1,j+a[i+1])=min(f(i+1,j+a[i+1]),max(0,f(i,j)−a[i+1))

复杂度: O ( n ∗ 2000 ) O(n*2000) O(n∗2000)

code:

#include <bits/stdc++.h>using namespace std;typedef long long ll;const int maxn=2005;
const int inf=0x3f3f3f3f;int n,a[10005],f[10005][maxn];int main(){int t;scanf("%d",&t);while(t--){scanf("%d",&n);for(int i=1;i<=n;i++) scanf("%d",&a[i]);for(int i=0;i<=n;i++){for(int j=0;j<=2000;j++) f[i][j]=inf;}f[0][0]=0;for(int i=0;i<n;i++){for(int j=0;j<=2000;j++){if(f[i][j]==inf) continue;int k=j+a[i+1];if(k<=2000) f[i+1][k]=min(f[i+1][k],max(0,f[i][j]-a[i+1]));k=max(0,j-a[i+1]);f[i+1][k]=min(f[i+1][k],f[i][j]+a[i+1]);}}int ans=inf;for(int i=0;i<=2000;i++) ans=min(ans,i+f[n][i]);printf("%d\n",ans);}return 0;
}

这道题我认为对于没有接触过这种优化技巧的人来说属是是精妙。在我们分析出当前做法下几个必须记录的状态但是复杂度不够时,我们还可以从状态之间的关系去做妥协,甚至与用DP值来表述一维状态。这类技巧相比单调队列,斜率优化这种个人认为更具思维性和技巧性,需要去多琢磨思考。。

做法2:贪心(bitset优化+二分

可以发现,如果留给这段线段的空间越大,显然 他们的发挥空间也越大,越容易合理,考虑二分答案。

但是怎么check成了这道题的瓶颈。。实在不好想出一个贪心策略。于是我们根据其本质就是插入线段这一道理,考虑直接去维护这个它。设 f ( i , j ) f(i,j) f(i,j) 代表第 i i i 个位置能否成为第 j j j 条线段的结尾,也就是说第 i i i 个位置是否有可能成为第 j + 1 j+1 j+1 条线段的开始

  • 这里强调一下,如果 f ( i , j ) = 1 f(i,j)=1 f(i,j)=1 只代表第 i i i 个位置可能成为 j + 1 j+1 j+1 条线段的起点,但是以此为起点第 j + 1 j+1 j+1 条线段不一定放得下

当前二分值为 x x x 时,代表我们的覆盖空间不能超过 x x x,bitset的大小应该是 [ 0 , x ] [0,x] [0,x] 表示点的位置,接着我们按照上述方式维护,每次只需要把左移右移的结果并起来就行吗,即 s = ( s < < a [ i ] ) ∣ ( s > > a [ i ] ) s=(s<<a[i])|(s>>a[i]) s=(s<<a[i])∣(s>>a[i])。

  • 这里对于 bitset 初始值的设定是一个问题,可能第一眼觉得应该设置在 s [ 0 ] s[0] s[0],那么我们没法像 0 0 0 的左侧转移,显然不合理。那么接着可能会有想法:放在 m i d / 2 mid/2 mid/2 的位置,那么对于 m i d / 2 < a i ≤ m i d mid/2<a_i \leq mid mid/2<ai​≤mid 的数据显然又不行了。
  • 实际上,根据我们上述定义,我们应该把 b i t s e t bitset bitset 每一位都设置为 1 1 1。并且这不影响我们的结果,且能保证答案的正确性。还是要回到那个道理,我们不在意起始位置具体在哪儿,完全可以把问题脱离数轴,我们只在乎这一次次的跳跃偏移覆盖范围能否控制在一定值内。

这样来说 check 的复杂度是 O ( 2000 ) O(2000) O(2000),然后实际答案范围是 ( 0 , 2000 ] (0,2000] (0,2000],那么我们甚至都不需要二分了,直接跑。。。从二分答案的思路转变到不需要二分hhh

复杂度: O ( 2000 ∗ 12 ) O(2000*12) O(2000∗12)

code:

#include <bits/stdc++.h>using namespace std;typedef long long ll;const int maxn=1e4+10;int n,a[maxn];bitset<2005> s,t;inline bool check(int x){s=t=0;for(int i=0;i<=x;i++) s.set(i);t=s;for(int i=1;i<=n;i++){s=((s<<a[i])|(s>>a[i]))&t;}return s.count();
}int main(){int t;scanf("%d",&t);while(t--){scanf("%d",&n);for(int i=1;i<=n;i++) scanf("%d",&a[i]);int l=0,r=2000,ans;while(l<=r){int mid=l+r>>1;if(check(mid)) ans=mid,r=mid-1;else l=mid+1;}printf("%d\n",ans);}return 0;
}

CF1579G - Minimal Coverage(DP,贪心,二分)相关推荐

  1. CodeForces - 1579G Minimal Coverage(dp)

    题目链接:点击查看 题目大意:给出 nnn 个长度不同的木棍.设第 i−1i-1i−1 次放置木棍后的终点为 xxx,那么第 iii 个木棍有且仅有两种放置方法: 放到 [x+1,x+a[i]][x+ ...

  2. 【UVA - 10020 】Minimal coverage (贪心,区间覆盖问题)

    题干:(Uva题不给题干了) t组样例,每组首先给出一个M,然后给出一些线段(0 0结束),然后问怎么取能使得最少的线段覆盖区间[0, M]. Sample Input 2 1 -1 0 -5 -3 ...

  3. LIS最长上升子序列详解(动态规划、贪心+二分、树状数组)

    1.摘要: 关于LIS部分,本篇博客讲一下LIS的概念定义和理解,以及求LIS的三种方法,分别是O(n^2)的DP,O(nlogn)的二分+贪心法,以及O(nlogn)的树状数组优化的DP,最后附上几 ...

  4. [SCOI2005]栅栏(贪心+二分+dfs)难度⭐⭐⭐⭐

    [SCOI2005]栅栏(贪心+二分+dfs) P2329 [SCOI2005]栅栏 题目描述 农夫约翰打算建立一个栅栏将他的牧场给围起来,因此他需要一些特定规格的木材.于是农夫约翰到木材店购买木材. ...

  5. 洛谷P2507 [SCOI2008]配对 题解(dp+贪心)

    洛谷P2507 [SCOI2008]配对 题解(dp+贪心) 标签:题解 阅读体验:https://zybuluo.com/Junlier/note/1299251 链接题目地址:洛谷P2507 [S ...

  6. 贪心/二分查找 BestCoder Round #43 1002 pog loves szh II

    题目传送门 1 /* 2 贪心/二分查找:首先对ai%=p,然后sort,这样的话就有序能使用二分查找.贪心的思想是每次找到一个aj使得和为p-1(如果有的话) 3 当然有可能两个数和超过p,那么an ...

  7. 【BZOJ3174】【codevs25442075】拯救小矮人,DP+贪心

    Time:2016.07.19 Author:xiaoyimi 转载注明出处谢谢 传送门1 传送门2 传送门3 思路: 比较神的DP "贪心确定DP的状态"--reflash 写了 ...

  8. BZOJ 1046: [HAOI2007]上升序列【贪心+二分状态+dp+递归】

    1046: [HAOI2007]上升序列 Time Limit: 10 Sec  Memory Limit: 162 MB Submit: 4987  Solved: 1732 [Submit][St ...

  9. Minimal coverage (贪心,最小覆盖)

    题目大意:先确定一个M, 然后输入多组线段的左端和右端的端点坐标,然后让你求出来在所给的线段中能够 把[0, M] 区域完全覆盖完的最少需要的线段数,并输出这些线段的左右端点坐标. 思路分析: 线段区 ...

最新文章

  1. 机器学习奠基人Michael Jordan:下代技术是融合经济学,解读2项重要进展
  2. Java 9 揭秘(14. HTTP/2 Client API)
  3. hashcode值一样对象一定相同吗_HashMap实现原理分析(面试问题:两个hashcode相同 的对象怎么存入hashmap的)...
  4. spark整合MySQL
  5. ironpython怎么编译_将IronPython WPF项目编译为
  6. python颜色大全
  7. 成都国税打造全能“电子税务局”
  8. STM32基础8--通用定时器(PWM控制LED)
  9. 电脑没网络在计算机哪,电脑没有wifi连接功能在哪里设置
  10. Unity GUI 中文显示
  11. 三维动画在计算机上的应用,三维动画运用领域有哪些地方?
  12. Mysql 数据补位
  13. 做phodal的御用编辑,其实我是拒绝的
  14. 切换电脑计算机名称软件,多电脑切换器
  15. python全局变量(模块法和global)
  16. 实现android广告栏效果
  17. 磨金石教育摄影技能干货分享|乡愁摄影作品欣赏——传统建筑篇
  18. python 图片合成视频
  19. 【OpenCV入门教程之三】 图像的载入,显示和输出 一站式完全解析
  20. #计算机应用与技巧分享 #应用推荐 #录屏 Captura 免费开源的屏幕录制工具

热门文章

  1. 【RPA之家转载】人资RPA:实现个税管理自动化
  2. 通达信自动交易软件 z
  3. 优秀企业文化学习(学习节选)
  4. 【ERP】常见错误收集
  5. 使用stm32解析富斯i6接收机(IBUS)
  6. Noise2Void 的一些学习总结
  7. 《《世界因你而不同》》——读后感
  8. bartender4没有权限打开怎么办?解决bartender给不了权限问题
  9. 《入门练习》1、长方形周长和面积
  10. 免费矢量图标网站都有哪些,推荐这10个