「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