题意:
有n个点,每个点都有一个点权,每个点都有一条向右到达n点中一点的不等长线段。
总价值这样计算:
若取这条线段,就将该线段的最左端点的权值放入总花费中。
求:
能覆盖n个点的最小花费。
解:
1.线段树+dp
对于线段上的每一个点,都从左向右一一处理。对于点x,取所有能到达他的线段当中花费最小的。对于x后面的y也是一样的。这样就能保证以最小的权值去覆盖整个。但是,y能被1所覆盖是有条件的。就是必须取x点。方程是dp[i]=min(dp[i],dp[i-day-1]+a[i-day])
反过来讲就是,每处理一个点i,就用自己dp[i-1]+p[i]去更新它之后的点。dp[i-1]表示到点i-1为止覆盖1~i-1的最小花费,dp[i-1]+p[i]即表示在第i天不要之前的了,重新买个西瓜,看看在这个西瓜的有效期限内能否使后面的花费变小。
注意: 第一个点的权值是肯定包括在总花费里面的,因为经过他的线段只有一条。从题意上来说,第一天的西瓜必须买,不然那天就没得吃了。
最后的答案就是取到最后一个点的最小价值。
接下来就应该想如何用线段树来完成。用线段树处理有什么优势?
这题是单点更新,区间询问。
建树就是将每个点都赋值为无穷大。
从左向右每处理一个点,是否真的有必要去更新这个点之后所能到达的所有点呢?
可以不的。在处理第i个点时,只去更新这个点所能到达最远点的值,用i-1到n中的最小值(询问)加上i这点的花费去更新。
用i-1到n中的最小值是因为在i-1那时,i-1到n这些点的最小值一定是覆盖1~(i-1) 的最小花费,即买到i-1这点所需最少花费;加上i这点的花费是因为加上才能覆盖最远点。
感觉这样的解法很神奇。
WA了n次,最后查出原因是因为输出写成I64d,写成lld就过了。wondering。
2. 优先队列
其实就是在弄出i-n的最小值的时候用个优先队列。方法是一样的。
#include <cstdio>
#include <algorithm>
#define maxn 50010
#define lson l,m, rt << 1
#define rson m + 1, r, rt << 1| 1
#define ls rt <<1
#define rs rt << 1 | 1
#define inf 5000000010
typedef long long LL;
using namespace std;
int n,p[maxn],t[maxn];
LL Min[maxn << 2],val;
void build(int l , int r, int rt){
Min[rt] = inf;
if(l == r) return;
int m = (l + r) >> 1;
build(lson); build(rson);
}
void update(int &pos, LL &cc ,int l, int r, int rt){
if(l == r){
Min[rt] = min(Min[rt],cc); return;
}int m = (l + r) >> 1;
if(pos <= m) update(pos,cc,lson);
else update(pos,cc,rson);
Min[rt] = min(Min[ls],Min[rs]);
}
LL query(int &L, int &R,int l, int r, int rt){
if(L <= l && r <= R){
return Min[rt];
}int m = (l + r) >> 1;
LL x = inf, y = inf;
if(L <= m) x = query(L,R,lson);
if(R > m) y = query(L,R,rson);
return min(x,y);
}
int main(){
while(scanf("%d",&n)!= EOF){
for(int i = 1; i <= n; i ++)
scanf("%d",&p[i]);
for(int i = 1; i <= n; i ++)
scanf("%d",t + i);
build(1,n,1); val = 0;
for(int i = 1; i <= n; i ++){
int lastday = i - 1 + t[i];
lastday = lastday > n ? n: lastday;
val += (LL)p[i];
update(lastday,val,1,n,1);
val = query(i,n,1,n,1);
}printf("%lld\n",val);
}
return 0;
}
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <queue>
#define N 50005
using namespace std;
long long Min[N<<2];
int p[N],last[N];
long long dp[N];
struct node{
long val;
int last;
bool operator <(const node& cmp) const{
return val > cmp.val;//注意,优先队列这里用的是>, 跟结构体数组是相反的。
}
};
int main(){
int n;
while(scanf("%d",&n)!=EOF){
for(int i = 1;i <= n; i ++)
scanf("%d",&p[i]);
for(int i = 1;i <= n; i ++)
scanf("%d",&last[i]);
priority_queue<node> q;
node tmp;
dp[1] = p[1];
tmp.val = p[1]; tmp.last = last[1]; q.push(tmp);
for(int i = 2; i <= n; i ++){
tmp.val = dp[i - 1] + p[i];
tmp.last = last[i] + i - 1;
q.push(tmp);
while(q.top().last < i) q.pop();
dp[i] = q.top().val;
}
printf("%lld\n",dp[n]);
}
return 0;
}