「BZOJ1537」Aut – The Bus(变形Dp+线段树/树状数组 最优值维护)

   网格图给予我的第一反应就是一个状态 f[i][j] 表示走到第 (i,j) 这个位置的最大价值。

  由于只能往下或往右走转移就变得显然了:

    f[i][j]=max{f[i-1][j], f[i][j-1]}+a[i][j]

  但是面对庞大的数据范围,再优秀的电脑也无法驾驭 百亿亿 的时间复杂度,与 百亿亿 的空间复杂度。

 

  其实只要稍加思考我们就可以发现,枚举那些没有价值的 (x,y) 的坐标是多余的,所以我们只需要枚举有价值的点,在进行Dp转移就会起到加速。

  因此新的状态即为:f[i] 表示到达第 i 个点的最大价值(Ps:为了避免后效性应先把一维的坐标进行排序,原因即是当前的状态只可能从最近的上一次转移而来),所以转移即为:

    f[i]=max{f[j]}+a[i] ( 0<j≤i-1,x[j]≤x[i],y[j]≤y[i] )

  虽然此时它优秀了不少,但是面对 1≤ n ≤10^9 数据范围,它还是离 AC 此题相隔甚远QAQ。

 

  所以对于现在的转移,i 的枚举无法避免,但是对于 MAX 的最优求解仍有极大的优化空间。所以此时我们面临的问题是: f[j] 的转移状态能否从一些连续的数值范围内得到呢,其实仔细思考一下就可以得到一个规律,对于一个 f[i] 它的坐标 (x[i], y[i]) ,假设我们将 x[i] 进行了排序,显然仍以的 x[i] 都满足转移的条件,所以我们转移只可能来自 1 ˜ y[i]-1 这个范围内,所以只要快速求得1 ˜ y[i]-1 内的转移的最大值,就可解决此题!

  显然我们可以维护一个线段树,来维护前 k 种 y[i] 的转移的最大值(由于它是再求前缀的最优值,我们也可以用树状数组来维护)。

  在维护时,我们可以通过 二分查找 快速求得当前的 y[i] 位于第几小的纵向坐标,再根据一种优秀的数据结构来解决最优值的维护。

  所以再最后献上本人的专属代码(线段树版本)与网络大佬的代码(树状数组版本):

 

  

 1 首先是蒟蒻本人的专属代码:
 2 
 3 #include<bits/stdc++.h>
 4 using namespace std;
 5 const int MAXN_TREE=400010;
 6 const int MAXN=100010;
 7 
 8 inline int read(){
 9     int ret=0;
10     char ch=getchar();
11     while (ch<'0' || ch>'9') ch=getchar();
12     while (ch>='0' && ch<='9') ret=ret*10+ch-'0', ch=getchar();
13     return ret;
14 }
15 // 读入优化
16 
17 struct You{
18     int x, y, val;
19 }a[MAXN];
20 int b[MAXN], tree[MAXN_TREE], Ans, n;
21 
22 inline bool cmp(const You&x, const You&y){
23     return x.x<y.x || (x.x==y.x && x.y<y.y);
24 }
25 
26 inline int find(int x){
27     int l=1,r=n;
28     while(l<=r){
29         int mid=(l+r)>>1;
30         if(b[mid]<x)l=mid+1;
31             else if(b[mid]==x)return mid;
32                 else r=mid-1;
33     }
34 }
35 // 二分查找
36 
37 inline int query(int root, int l, int r, int x, int y){
38     if (y<l || x>r) return 0;
39     if (l>=x && r<=y) return tree[root];
40     int mid=(l+r)>>1;
41     return max(query(root+root, l, mid, x, y), query(root+root+1, mid+1, r, x, y));
42 }
43 // 线段树区间查找
44 
45 inline void change(int root, int l, int r, int x, int sum){
46     if (r<x || l>x) return;
47     if (l==r && l==x){
48         tree[root]=sum;
49         return;
50     }
51     int mid=(l+r)>>1;
52     change(root+root, l, mid, x, sum);
53     change(root+root+1, mid+1, r, x, sum);
54     tree[root]=max(tree[root+root], tree[root+root+1]);
55 }
56 //线段树单点修改
57 
58 int main(){
59     n=read(), n=read(), n=read();
60     for (int i=1; i<=n; i++) a[i].x=read(), a[i].y=read(), a[i].val=read(), b[i]=a[i].y;
61     sort(a+1, a+1+n, cmp);
62     sort(b+1, b+1+n);
63     for (int i=1; i<=n; i++){
64         int Place=find(a[i].y);                  // 寻找当前 y[i] 为第Place小的横向坐标
65         int Sum=a[i].val+query(1, 1, n, 1, Place);
66         change(1, 1, n, Place, Sum);        //插入当前转移的值
67         Ans=max(Ans, Sum);
68     } 
69     printf("%d\n", Ans);
70     return 0;
71 }
大佬的代码,来自与http://hzwer.com/3248.html/
未经允许复制下来,希望大佬不要追究法律责任QWQ,所以我就不做详细
介绍了:
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; inline int read() { int x=0;char ch=getchar(); while(ch<'0'||ch>'9')ch=getchar(); while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x; } int n,ans; int t[100005],hash[100005]; struct data{int x,y,p;}a[100005]; inline bool cmp(data a,data b) {if(a.x==b.x)return a.y<b.y;return a.x<b.x;} int find(int x) { int l=1,r=n; while(l<=r) { int mid=(l+r)>>1; if(hash[mid]<x)l=mid+1; else if(hash[mid]==x)return mid; else r=mid-1; } } inline int lowbit(int x){return x&(-x);} void change(int x,int val) { for(int i=x;i<=n;i+=lowbit(i)) t[i]=max(val,t[i]); } int ask(int x) { int tmp=0; for(int i=x;i>0;i-=lowbit(i)) tmp=max(tmp,t[i]); return tmp; } int main() { n=read(),n=read(),n=read(); for(int i=1;i<=n;i++) { a[i].x=read();a[i].y=read();a[i].p=read(); hash[i]=a[i].y; } sort(hash+1,hash+n+1); sort(a+1,a+n+1,cmp); int tmp,pos; for(int i=1;i<=n;i++) { pos=find(a[i].y); tmp=a[i].p+ask(pos); change(pos,tmp); ans=max(ans,tmp); } printf("%d",ans); return 0; } 太优美了,蒟蒻在线膜拜!!!

第二篇加油加油,奋斗ing!!!QAQ

  

posted @ 2018-09-08 23:52  等傻子仙女的傻子  阅读(207)  评论(0编辑  收藏  举报