[线段树优化DP][ZJOI2010]基站选址
题目描述
有N个村庄坐落在一条直线上,第i(i>1)个村庄距离第1个村庄的距离为Di。需要在这些村庄中建立不超过K个通讯基站,在第i个村庄建立基站的费用为Ci。如果在距离第i个村庄不超过Si的范围内建立了一个通讯基站,那么就村庄被基站覆盖了。如果第i个村庄没有被覆盖,则需要向他们补偿,费用为Wi。现在的问题是,选择基站的位置,使得总费用最小。
输入格式
输入文件的第一行包含两个整数N,K,含义如上所述。
第二行包含N-1个整数,分别表示D2,D3,…,DN ,这N-1个数是递增的。
第三行包含N个整数,表示C1,C2,…CN。
第四行包含N个整数,表示S1,S2,…,SN。
第五行包含N个整数,表示W1,W2,…,WN。
输出格式
输出文件中仅包含一个整数,表示最小的总费用。
输入输出样例
3 2
1 2
2 3 2
1 1 0
10 20 30
输出
4
说明/提示
40%的数据中,N<=500;
100%的数据中,K<=N,K<=100,N<=20,000,Di<=1000000000,Ci<=10000,Si<=1000000000,Wi<=10000。
Solution
考虑朴素DP
f[i][j]表示只考虑前i个村庄且必在i村建基站,已经建设j个基站的最小费用
这样会忽略最后一个基站对之后村庄的影响
所以我们在无穷远处建一个假村假站,无费用,
就没有以上弊端,max f[n][1--k]即为答案
转移方程 f[i][j]=min(f[k][j-1]+cost[k][i])
cost[k][i]为在k和i建基站,中间不建的赔偿花费,是可计算的常数
j这一维可以滚动掉,我们得到了时间复杂度O(n*n*k)的朴素DP算法
考虑优化,取最小值显然可以用线段树维护
问题转化为如何计算cost[k][i]
考虑一个村庄何时可以获得赔偿 显然是枚举到向右距离超过si的村庄, 且用来转移的k距离超过si时1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 const int N=20005,inf=0x3f3f3f3f; 5 int n,k,d[N],c[N],s[N],w[N],f[N]; 6 7 int num,last[N],nxt[N],ord[N]; 8 inline void add(int x,int hao){nxt[++num]=last[x]; last[x]=num; ord[num]=hao;} 9 int st[N],ed[N]; 10 inline void pre() 11 {for(int i=1;i<=n;i++) 12 {st[i]=lower_bound(d+1,d+n+1,d[i]-s[i])-d; 13 ed[i]=upper_bound(d+1,d+n+1,d[i]+s[i])-d-1; 14 add(ed[i],i); 15 } // 在往ed后就不能覆盖这些基站了,要考虑赔偿 16 } 17 18 19 struct point{int l,r,minn,lazy;}t[4*N]; 20 21 22 inline void pushup(int i) {t[i].minn=min(t[2*i].minn,t[2*i+1].minn); } 23 24 inline void pass(int i,int x) {t[i].minn+=x; t[i].lazy+=x;} 25 26 inline void pushdown(int i) 27 {if(!t[i].lazy) return; 28 pass(2*i,t[i].lazy); pass(2*i+1,t[i].lazy); 29 t[i].lazy=0; 30 } 31 32 33 34 void build(int i,int l,int r) 35 {t[i].l=l; t[i].r=r; t[i].lazy=0; 36 37 if(l==r) {t[i].minn=f[r]; return;} 38 39 int mid=t[i].l+t[i].r>>1; 40 build(2*i,l,mid); build(2*i+1,mid+1,r); 41 42 pushup(i); 43 } 44 45 void add(int i,int l,int r,int x) 46 {if(l>r) return; 47 if(l<=t[i].l && t[i].r<=r) {pass(i,x); return;} 48 49 int mid=t[i].l+t[i].r>>1; pushdown(i); 50 51 if(l<=mid) add(2*i,l,r,x); 52 if(mid<r) add(2*i+1,l,r,x); 53 54 pushup(i); 55 } 56 57 58 int ask(int i,int l,int r) 59 {if(l>r) return 0; 60 if(l<=t[i].l && t[i].r<=r) return t[i].minn; 61 int mid=t[i].l+t[i].r>>1; pushdown(i); 62 63 if(r<=mid) return ask(2*i,l,r); 64 if(mid<l) return ask(2*i+1,l,r); 65 return min(ask(2*i,l,r),ask(2*i+1,l,r)); 66 pushup(i); 67 } 68 inline void hehe() 69 { 70 int cmp=0; 71 // 手动处理只能选一个基站的边界DP值,相当于每次必须从f[0]=0转移 72 for(int i=1;i<=n;i++) 73 {f[i]=cmp+c[i]; 74 75 for(int j=last[i];j;j=nxt[j])cmp+=w[ord[j]]; 76 } 77 int ans=f[n]; 78 for(int i=2;i<=k;i++) 79 {build(1,1,n); 80 81 for(int j=1;j<=n;j++) 82 {if (i>j) f[j]=inf; 83 else f[j]=ask(1,i-1,j-1)+c[j]; 84 85 for(int p=last[j];p;p=nxt[p]) 86 add(1,1,st[ord[p]]-1,w[ord[p]]); 87 88 } 89 ans=min(ans,f[n]); 90 } 91 printf("%d",ans); 92 } 93 int main() 94 { 95 scanf("%d%d",&n,&k); k++; 96 for(int i=2;i<=n;i++) scanf("%d",&d[i]); 97 for(int i=1;i<=n;i++) scanf("%d",&c[i]); 98 for(int i=1;i<=n;i++) scanf("%d",&s[i]); 99 for(int i=1;i<=n;i++) scanf("%d",&w[i]); 100 n++; d[n]=w[n]=inf; 101 //当我们推导i时,我们只考虑了它和前面的基站产生的影响 102 //这时对于最后一个基站我们不会考虑它和之后的村庄产生的影响 103 //则我们可以在最后增加一个村庄 104 //保证它必定被作为基站(无建设费用)且不对前面产生影响 105 //这样就不会有遗漏的了 106 pre(); 107 hehe(); 108 return 0; 109 }
原文地址:https://www.cnblogs.com/YuXiaoze/p/11858630.html
- 小程序-实现竖排文字
- 【深度学习量化投资】RNNs在股票价格预测的应用基于Keras
- 关于webview调用js出现has no method 'toString'
- 深入学习Apache Spark和TensorFlow
- 搭建 WPF 上的 UI 自动化测试框架
- ttf设置文字字体
- R语言构建追涨杀跌量化交易模型(附源代码)
- Apache Spark中使用DataFrame的统计和数学函数
- android进程 清理及activity栈管理
- 机器学习模型的变量评估和选择基于技术指标『深度解析』
- Picasso and Android-Universal-Image-Loader缓存框架
- 解决ListView嵌套ListView遇到的问题
- 《OEA - 实体扩展属性系统 - 设计方案说明书》
- webview与js的相互交互
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 【52期】记一道简单的Java面试题,但答错率很高!
- 面向隐私 AI 的 TensorFlow 深度定制化实践
- 别再问如何用Python提取PDF内容了!
- gff文件转换成gtf文件
- R的save,load函数和 .rda文件
- 10行Python代码自动清理电脑内重复文件,解放双手!
- 【一天一大 lee】二叉树的后序遍历 (难度:中等) - Day20200929
- 一日一技:在Golang下如何相对简单地开发爬虫?
- 原创 | 一文读懂正态分布与贝塔分布
- 收款神器!解读聚合收款码背后的原理
- 短视频商城源码,滑动时渐变效果实现
- CICD实战——服务自动测试
- MySQL 案例:left join 与 BUG
- WordPress缩略图出现A TimThumb error has occured解决办法
- 2020-09-30:谈谈内存对齐。