hdu 3564(线段树+LIS)
2016-03-27 12:00
531 查看
题意:给出1~n的插入顺序,要求每次插入之后的LIS
解题思路:这道题确实挺难想的,我最开始想用树状数组每插入一个数就更新一次,如果这么想,那么你就输了。它这里的想法是先将1-n的最终位置都保存起来,然后再一个个去找LIS。这里有点离线算法的意思,至少了解到更新时可以先别急着处理。还有这里要总结一种线段树的用法,就是在空格处去填充数字,确实结合了这道题的特点把线段树用的很灵活。。。
参考的博客:http://blog.csdn.net/libin56842/article/details/13095801
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 100005;
struct Segment
{
int l,r;
int cnt; //cnt记录空位的数量
}tree[maxn<<2];
int n,len,pos[maxn],ans[maxn],dp[maxn];
void build(int rt,int l,int r)
{
tree[rt].l = l, tree[rt].r = r;
tree[rt].cnt = 1;
if(l == r) return;
int mid = (l + r) >> 1;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
tree[rt].cnt = tree[rt<<1].cnt + tree[rt<<1|1].cnt;
}
void insert(int rt,int x,int m) //表示m要插入x的位置
{
if(tree[rt].l == tree[rt].r)
{
ans[m] = tree[rt].l;
tree[rt].cnt = 0;
return;
}
tree[rt].cnt--; //要插入一个数,表示这段区间内有一个空会被占
if(x <= tree[rt<<1].cnt)
insert(rt<<1,x,m);
else insert(rt<<1|1,x-tree[rt<<1].cnt,m);
}
int binsearch(int val)
{
int l = 1, r = len, mid;
while(l <= r)
{
mid = (l + r) >> 1;
if(val > dp[mid])
l = mid + 1;
else r = mid - 1;
}
return l;
}
int main()
{
int t,cas = 1;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i = 1; i <= n; i++)
{
scanf("%d",&pos[i]);
pos[i]++;
dp[i] = 0;
}
build(1,1,n);
for(int i = n; i >= 1; i--) //必须要逆着遍历,因为最后一个加入的数是可以确定位置的,倒数第二个数在最后一个数确定的情况下再找位置,以此类推
insert(1,pos[i],i); //也就是说,越往后加入进来的数,它选择位置的权利更大,这是种逆向思维
printf("Case #%d:\n",cas++);
len = 0;
for(int i = 1; i <= n; i++)
{
int k = binsearch(ans[i]);
len = max(len,k);
dp[k] = ans[i];
printf("%d\n",len);
}
printf("\n");
}
return 0;
}
解题思路:这道题确实挺难想的,我最开始想用树状数组每插入一个数就更新一次,如果这么想,那么你就输了。它这里的想法是先将1-n的最终位置都保存起来,然后再一个个去找LIS。这里有点离线算法的意思,至少了解到更新时可以先别急着处理。还有这里要总结一种线段树的用法,就是在空格处去填充数字,确实结合了这道题的特点把线段树用的很灵活。。。
参考的博客:http://blog.csdn.net/libin56842/article/details/13095801
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 100005;
struct Segment
{
int l,r;
int cnt; //cnt记录空位的数量
}tree[maxn<<2];
int n,len,pos[maxn],ans[maxn],dp[maxn];
void build(int rt,int l,int r)
{
tree[rt].l = l, tree[rt].r = r;
tree[rt].cnt = 1;
if(l == r) return;
int mid = (l + r) >> 1;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
tree[rt].cnt = tree[rt<<1].cnt + tree[rt<<1|1].cnt;
}
void insert(int rt,int x,int m) //表示m要插入x的位置
{
if(tree[rt].l == tree[rt].r)
{
ans[m] = tree[rt].l;
tree[rt].cnt = 0;
return;
}
tree[rt].cnt--; //要插入一个数,表示这段区间内有一个空会被占
if(x <= tree[rt<<1].cnt)
insert(rt<<1,x,m);
else insert(rt<<1|1,x-tree[rt<<1].cnt,m);
}
int binsearch(int val)
{
int l = 1, r = len, mid;
while(l <= r)
{
mid = (l + r) >> 1;
if(val > dp[mid])
l = mid + 1;
else r = mid - 1;
}
return l;
}
int main()
{
int t,cas = 1;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i = 1; i <= n; i++)
{
scanf("%d",&pos[i]);
pos[i]++;
dp[i] = 0;
}
build(1,1,n);
for(int i = n; i >= 1; i--) //必须要逆着遍历,因为最后一个加入的数是可以确定位置的,倒数第二个数在最后一个数确定的情况下再找位置,以此类推
insert(1,pos[i],i); //也就是说,越往后加入进来的数,它选择位置的权利更大,这是种逆向思维
printf("Case #%d:\n",cas++);
len = 0;
for(int i = 1; i <= n; i++)
{
int k = binsearch(ans[i]);
len = max(len,k);
dp[k] = ans[i];
printf("%d\n",len);
}
printf("\n");
}
return 0;
}
相关文章推荐
- C#数据结构之顺序表(SeqList)实例详解
- Lua教程(七):数据结构详解
- 解析从源码分析常见的基于Array的数据结构动态扩容机制的详解
- C#数据结构之队列(Quene)实例详解
- C#数据结构揭秘一
- C#数据结构之单链表(LinkList)实例详解
- 数据结构之Treap详解
- 用C语言举例讲解数据结构中的算法复杂度结与顺序表
- C#数据结构之堆栈(Stack)实例详解
- C#数据结构之双向链表(DbLinkList)实例详解
- JavaScript数据结构和算法之图和图算法
- Java数据结构及算法实例:冒泡排序 Bubble Sort
- Java数据结构及算法实例:插入排序 Insertion Sort
- Java数据结构及算法实例:考拉兹猜想 Collatz Conjecture
- java数据结构之java实现栈
- java数据结构之实现双向链表的示例
- Java数据结构及算法实例:选择排序 Selection Sort
- Java数据结构及算法实例:朴素字符匹配 Brute Force
- Java数据结构及算法实例:汉诺塔问题 Hanoi
- Java数据结构及算法实例:快速计算二进制数中1的个数(Fast Bit Counting)