[CodeVS3012]线段覆盖4(DP+二分)
2016-11-07 16:00
369 查看
=== ===
这里放传送门=== ===
题解
这道题如果是O(n2)的DP是比较好想的,用f[i]表示选了前i条线段并且第i条必选的最大价值,每次枚举一个线段i的时候枚举它前面不和它重叠的线段j,用f[j]+val[i]更新。但是数据范围这么大就要考虑优化了。优化这种DP的话比较好想的思路就是省掉内层循环。内层循环干的事情有两个,一个是找出1..i-1范围内不和第i条线段重合的线段,二是在这些合法的线段里面找一个f最大的来更新f[i]。那么如果把所有线段按照右端点排序,那么只要找到了右端点小于i的左端点的最大的那个j,那么1..j显然都是符合要求的线段。而我们要查询的最大值就变成了一个前缀最大值,可以用一个数组直接维护。右端点排序以后寻找那个最大的j就可以用二分来解决。时间复杂度O(nlogn)。代码
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int n; long long f[1000010],v[1000010],tot=0; struct seg{ int l,r,c; }a[1000010]; int comp(seg a,seg b) {return a.r<b.r;} long long max(long long a,long long b) {return (a>b)?a:b;} int divide(int h,int t,int n) { int mid=(h+t)>>1; if (h==t) return h; if ((h+t)%2==1) ++mid; if (a[mid].r<=a .l) return divide(mid,t,n); else return divide(h,mid-1,n); } int main() { memset(f,0,sizeof(f)); memset(v,0,sizeof(v)); scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%d%d%d",&a[i].l,&a[i].r,&a[i].c); sort(a+1,a+n+1,comp); for (int i=1;i<=n;i++) { int j=divide(0,i-1,i); f[i]=v[j]+a[i].c; v[i]=max(f[i],v[i-1]); }//以下为原始DP代码 /* for (int j=0;j<i;j++) if ((a[j].r<=a[i].l)&&(a[i].c+f[j]>f[i])) f[i]=f[j]+a[i].c;*/ printf("%lld\n",v ); return 0; }
偏偏在最后出现的补充说明
诶这题好像是我去年NOIP完了以后做的但是现在才扒拉出来写题解23333相关文章推荐
- codevs 3012 线段覆盖4(dp+二分优化)
- CODEVS3037 线段覆盖 5[序列DP 二分]
- CodeVS 3012 & 3037 线段覆盖4 & 5
- CODEVS 3012 线段覆盖 4
- codevs 3027 线段覆盖 2 (简单dp)
- codevs 3037 线段覆盖 5,codevs 3012 线段覆盖 4,codevs 3027 线段覆盖 2
- Codevs 3027 线段覆盖 2 序列dp
- Codevs 3012 线段覆盖 4
- Code[vs] 3027 线段覆盖 2(dp求和)
- codevs 1214 线段覆盖(贪心 or dp)
- 线段覆盖4(codevs 3012)
- codevs 3012 线段覆盖 4 & 3037 线段覆盖 5
- codevs3027 线段覆盖2(DP)
- 【codevs3012】线段覆盖4
- codevs3027 线段覆盖2(DP)
- Codevs 3027 线段覆盖2
- CODE[VS]_1214 线段覆盖问题
- 【基础练习】【贪心】codevs1214 线段覆盖题解
- [codevs1513][BZOJ1863]皇帝的烦恼(二分+dp)
- 【基础练习】【贪心】codevs1214 线段覆盖题解