BZOJ 1835 base 基站选址(DP 线段树)
来源:互联网 发布:查淘宝买家退货率 编辑:程序博客网 时间:2024/05/16 09:16
1835: [ZJOI2010]base 基站选址
Time Limit: 100 Sec Memory Limit: 64 MB
有N个村庄坐落在一条直线上,第i(i>1)个村庄距离第1个村庄的距离为Di。需要在这些村庄中建立不超过K个通讯基站,在第i个村庄建立基站的费用为Ci。如果在距离第i个村庄不超过Si的范围内建立了一个通讯基站,那么就成它被覆盖了。如果第i个村庄没有被覆盖,则需要向他们补偿,费用为Wi。现在的问题是,选择基站的位置,使得总费用最小。 输入数据 (base.in) 输入文件的第一行包含两个整数N,K,含义如上所述。 第二行包含N-1个整数,分别表示D2,D3,…,DN ,这N-1个数是递增的。 第三行包含N个整数,表示C1,C2,…CN。 第四行包含N个整数,表示S1,S2,…,SN。 第五行包含N个整数,表示W1,W2,…,WN。
Input
输出文件中仅包含一个整数,表示最小的总费用。
Output
3 2 1 2 2 3 2 1 1 0 10 20 30
Sample Input
4
Sample Output
40%的数据中,N<=500;
100%的数据中,K<=N,K<=100,N<=20,000,Di<=1000000000,Ci<=10000,Si<=1000000000,Wi<=10000。
思路:
设f[i][j]表示第i个村建第j个基站的最小花费,转移方程:
f[i][j]=min{ f[k][j-1]+cost(k,i) } + c[i] ,j-1<=k<=i-1
cost(k,i)=sigma{ w[x] } k+1<=x<=i-1 , 且x未被覆盖
我们很容易发现其实j这一位是可以省去的,然而尽管如此我们还要继续降次。
方式很明确,就是维护一下min{ f[k][j-1]+cost(k,i) } ,可以用线段树维护
最外层循环j,考虑每一层i。
L[i],R[i]分别表示在i左右覆盖的范围,
假设我们已经求完了f[i][j]要求f[i+1][j],考虑那些恰可以被i覆盖到而不能被i+1覆盖到的,
即满足R[x]=i+1的点,将[1~y](R[y]=x)区间内的线段树值都加w[x],
因为前一个基站k位于[1~y](R[y]=x),也就是当f[k](1 <= k <= y,最后一个基站在k点时,无法覆盖到x),那么点x因不会被覆盖到需要做出赔偿(+w[x])。
求f[i]就是求区间[1~i-1]内线段树维护的最小值。
其中L[i],R[i]直接用二分函数求。
线段树支持区间操作区间查询。
总的时间复杂度为O(nmlogn)。
#include <cstdio>#include <cstring>#include <algorithm>#include <iostream>#define lc u<<1#define rc u<<1|1using namespace std;const int N = 20005, INF = 1e9 + 5;int n, k, d[N], c[N], s[N], w[N];int L[N], R[N], f[N];int head[N], idc;struct edge{ int to, next;}ed[N];void adde(int u, int v){ ed[++idc].to = v; ed[idc].next = head[u]; head[u] = idc;}struct node{ int mn, flag;}tree[N<<2];void updata(int u){ tree[u].mn = min(tree[lc].mn, tree[rc].mn);}void pushdown(int u){ if(tree[u].flag){ int addd = tree[u].flag; tree[lc].flag += addd; tree[lc].mn += addd; tree[rc].flag += addd; tree[rc].mn += addd; tree[u].flag = 0; }}void build(int u, int l, int r){ tree[u].flag = 0; if(l == r){ tree[u].mn = f[l]; return; } int mid = (l + r) >> 1; build(lc, l, mid), build(rc, mid+1, r); updata(u);}void modify(int u, int l, int r, int ll, int rr, int val){ if(ll > rr) return; if(ll<=l && r<=rr) { tree[u].flag += val; tree[u].mn += val; return; } pushdown(u); int mid = (l + r) >> 1; if(ll <= mid) modify(lc, l, mid, ll, rr, val); if(mid < rr) modify(rc, mid+1, r, ll, rr, val); updata(u);}int query(int u, int l, int r, int ll, int rr){ if(ll > rr) return 0; if(ll<=l && r<=rr) return tree[u].mn; pushdown(u); int mn = INF; int mid = (l + r) >> 1; if(ll <= mid) mn = min(mn, query(lc, l, mid, ll, rr)); if(mid < rr) mn = min(mn, query(rc, mid+1, r, ll, rr)); return mn;}void dp(){ int ans = INF, cc = 0; for(int i=1; i<=n; i++){//j==1时 f[i] = cc + c[i]; for(int k=head[i]; k; k=ed[k].next)//寻找恰好被i-1覆盖,不被i覆盖的点 cc += w[ed[k].to];//之后也永远不会再覆盖了,所以可以累加 } for(int j=2; j<=k; j++){ build(1, 1, n);//基站数目变化了,所以线段树也要重新建 for(int i=1; i<=n; i++){ f[i] = query(1,1,n,1,i-1) + c[i];//转移 for(int k=head[i]; k; k=ed[k].next){ int v = ed[k].to; modify(1, 1, n, 1, L[v]-1, w[v]);//新增的且永远不会再覆盖的点,加上代价 } } ans = min(ans, f[n]); } printf("%d", ans);}int main() { scanf("%d%d", &n, &k); for(int i=2; i<=n; i++) scanf("%d", &d[i]); for(int i=1; i<=n; i++) scanf("%d", &c[i]); for(int i=1; i<=n; i++) scanf("%d", &s[i]); for(int i=1; i<=n; i++) scanf("%d", &w[i]); n++; k++; d[n]=INF; w[n]=INF; for(int i=1; i<=n; i++){ L[i] = lower_bound(d+1, d+1+n, d[i]-s[i]) - d; R[i] = lower_bound(d+1, d+1+n, d[i]+s[i]) - d; if(d[R[i]] - d[i] > s[i]) R[i]--;//覆盖范围是一个开区间 adde(R[i],i);//建边,为了寻找恰好被i-1覆盖,不被i覆盖的点 } dp(); return 0;}
- BZOJ 1835 base 基站选址(DP 线段树)
- [BZOJ]1835: [ZJOI2010]base 基站选址 线段树+DP
- bzoj 1835: [ZJOI2010]base 基站选址 线段树优化dp
- bzoj 1835: [ZJOI2010]base 基站选址(线段树优化dp)
- bzoj1835 [ZJOI2010]base 基站选址(dp+线段树优化)
- bzoj 1835 基站选址(线段树优化Dp)
- bzoj1835基站选址(dp+线段树)
- bzoj 1835: [ZJOI2010]base 基站选址
- 【bzoj1835】【ZJOI2010】【基站选址】【dp+线段树】
- 1835: [ZJOI2010]base 基站选址
- 1835: [ZJOI2010]base 基站选址
- 1835: [ZJOI2010]base 基站选址
- 【动态规划03】bzoj1835基站选址(dp+线段树维护)
- ★【动态规划】【线段树】基站选址
- BZOJ1835 [ZJOI2010]base 基站选址
- [BZOJ1835][ZJOI2010]base 基站选址
- BZOJ1835 [ZJOI2010]base 基站选址
- ZJOI 2010 基站选址 DP+SegmentTree
- Serif PagePlus X9使用日历
- HDU2058 The sum problem【数学计算+枚举】
- 编译原理(六) LL(1)文法分析法(分析过程的C++实现)
- 【备忘】凉学长腾讯课堂2017高中二轮数学
- numpy中hstack() vstack() stack() dstack() vsplit() concatenate() 函数用法
- BZOJ 1835 base 基站选址(DP 线段树)
- 牛客《剑指Offer》 -- 从尾到头打印链表
- C语言的条件编译#if, #elif, #else, #endif、#ifdef, #ifndef
- 启动Android studio,报错:aapt.exe
- 编译原理(七) 算符优先分析法(构造算符优先关系表算法及C++实现)
- 面试题 32: 从1到n 整数中1出现的次数
- mybatis中#{}和${}的区别
- 项目之日期转换
- keil&MDK封装函数到lib中,并在程序中调用lib