题意:
有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; }