HDU 5489 Removed Interval (LIS,变形)
2015-10-01 15:09
387 查看
转载地址:http://www.bubuko.com/infodetail-1120211.html
题意:
给出一个n个元素的序列,要求从中删除任一段长度为L的连续子序列,问删除后的LIS是多少?(n<=10w, L<=n ,元素可能为负)
思路:
如果会O(nlogn)求普通LIS的算法,这道题将变得很简单。
首先按照求LIS的思路,当扫到元素a[i]并完成操作后,a[1->i]就是一段已经处理完成的序列,假设a[i+1]->a[i+L]这一段是将要删去的,那么将分成两段:a[0]->a[i]和a[i+L+1]->a
。
假设后一段以a[i+L+1]开头,而前段以一个小于a[i+L+1]的元素结尾,那么LIS应该为bac[前段]+len[后段]。
如何求bac[前段]?根据O(nlogn)求普通LIS的算法,当扫到a[i]且处理完的时候,就可以在当前d[]数组中找到a[i+L+1]应处的位置,用bac[i+L+1]标记起来其位置,知位置也就知其长度了。
如何求后段?当前段的bac[]数组全部求完时,按照O(nlogn)求普通LIS的算法,逆向再求一次此序列的LIS,当扫到a[j]时,求一下以a[j]开头的len[j]就行了。
其实就是枚举一个位置pos=i,然后删除其前面一段长度为L的子序列,分别求前段以a[i]结尾,求后段以a[i]开头的LIS。但是这样为什么是正确的?假设枚举到seq[i],后段中出现有LIS更长的,但是不是以seq[i]开头的怎么办?那么LIS更长的开头必定是一个seq[j]<seq[i],而你是从后面往前枚举的,以seq[j]开头的早就被你考虑过了。
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int maxn = 1e5 + 10;
const int inf = 0x3f3f3f3f;
int a[maxn], p[maxn], bac[maxn];
int cal(int n, int L)
{
if(n == L)
return 0;
int ans, len;
memset(bac, 0, sizeof(bac));
memset(p, -1, sizeof(p));
p[len=1] = a[1];
for(int i = 2; i+L <= n; i++)
{
if(a[i+L] > p[len])
bac[i+L] = len;
else
bac[i+L] = lower_bound(p+1, p+len+1, a[i+L]) - p -1;
if(a[i] > p[len])
p[++len] = a[i];
else
*lower_bound(p+1, p+len+1, a[i]) = a[i];
}
ans = max(len, bac
+1);
memset(p, 0x3f, sizeof(p));
p[len=n] = a
;
for(int i = n-1; i > L; i--)
{
int big;
if(a[i] < p[len])
{
p[--len] = a[i];
big = n - len + bac[i] + 1;
}
else
{
int pos = upper_bound(p+len, p+n+1, a[i]) - p - 1;
p[pos] = a[i];
big = n - pos + bac[i] + 1;
}
ans = max(ans, big);
}
return ans;
}
int main()
{
int T, n, L;
scanf("%d", &T);
for(int cas = 1; cas <= T; cas++)
{
scanf("%d%d", &n, &L);
for(int i = 1; i <= n; i++)
scanf("%d", &a[i]);
int ans = cal(n, L);
printf("Case #%d: %d\n", cas, ans);
}
return 0;
}
题意:
给出一个n个元素的序列,要求从中删除任一段长度为L的连续子序列,问删除后的LIS是多少?(n<=10w, L<=n ,元素可能为负)
思路:
如果会O(nlogn)求普通LIS的算法,这道题将变得很简单。
首先按照求LIS的思路,当扫到元素a[i]并完成操作后,a[1->i]就是一段已经处理完成的序列,假设a[i+1]->a[i+L]这一段是将要删去的,那么将分成两段:a[0]->a[i]和a[i+L+1]->a
。
假设后一段以a[i+L+1]开头,而前段以一个小于a[i+L+1]的元素结尾,那么LIS应该为bac[前段]+len[后段]。
如何求bac[前段]?根据O(nlogn)求普通LIS的算法,当扫到a[i]且处理完的时候,就可以在当前d[]数组中找到a[i+L+1]应处的位置,用bac[i+L+1]标记起来其位置,知位置也就知其长度了。
如何求后段?当前段的bac[]数组全部求完时,按照O(nlogn)求普通LIS的算法,逆向再求一次此序列的LIS,当扫到a[j]时,求一下以a[j]开头的len[j]就行了。
其实就是枚举一个位置pos=i,然后删除其前面一段长度为L的子序列,分别求前段以a[i]结尾,求后段以a[i]开头的LIS。但是这样为什么是正确的?假设枚举到seq[i],后段中出现有LIS更长的,但是不是以seq[i]开头的怎么办?那么LIS更长的开头必定是一个seq[j]<seq[i],而你是从后面往前枚举的,以seq[j]开头的早就被你考虑过了。
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int maxn = 1e5 + 10;
const int inf = 0x3f3f3f3f;
int a[maxn], p[maxn], bac[maxn];
int cal(int n, int L)
{
if(n == L)
return 0;
int ans, len;
memset(bac, 0, sizeof(bac));
memset(p, -1, sizeof(p));
p[len=1] = a[1];
for(int i = 2; i+L <= n; i++)
{
if(a[i+L] > p[len])
bac[i+L] = len;
else
bac[i+L] = lower_bound(p+1, p+len+1, a[i+L]) - p -1;
if(a[i] > p[len])
p[++len] = a[i];
else
*lower_bound(p+1, p+len+1, a[i]) = a[i];
}
ans = max(len, bac
+1);
memset(p, 0x3f, sizeof(p));
p[len=n] = a
;
for(int i = n-1; i > L; i--)
{
int big;
if(a[i] < p[len])
{
p[--len] = a[i];
big = n - len + bac[i] + 1;
}
else
{
int pos = upper_bound(p+len, p+n+1, a[i]) - p - 1;
p[pos] = a[i];
big = n - pos + bac[i] + 1;
}
ans = max(ans, big);
}
return ans;
}
int main()
{
int T, n, L;
scanf("%d", &T);
for(int cas = 1; cas <= T; cas++)
{
scanf("%d%d", &n, &L);
for(int i = 1; i <= n; i++)
scanf("%d", &a[i]);
int ans = cal(n, L);
printf("Case #%d: %d\n", cas, ans);
}
return 0;
}
相关文章推荐
- AngularJS双向绑定,手动实施观察
- NYOJ 14 场地安排
- 京东股权众筹投后总结和反思
- 京东股权众筹投后总结和反思
- 京东股权众筹投后总结和反思
- ZOJ-1649 Rescue
- Session对象的特点
- Ember
- hdu 5135__Little Zu Chongzhi's Triangles
- 记录我的进步- -
- getParameter和getAttribute的区别是什么?
- Mobile开发的饕餮盛宴-Zoomla!波CMS2 x2.1正式宣布
- ACdream: ACfun
- animate()
- ECharts+BaiduMap+HT for Web网络拓扑图应用
- Ubuntu安装单机版hadoop注意事项
- Xcode常见的项目文件介绍
- mysql
- 11384 - Help is needed for Dexter
- JSP四大作用域