Problem:bestcoder.hdu.edu.cn/contests/contest_chineseproblem.php?cid=718&pid=1002
acm.hdu.edu.cn/showproblem.php?pid=5748
题意:输入一个序列 a1,a2,…,an,要重构一个序列 b1,b2,…,bn,使得在各自的序列中,以对应位置的值 ai 和 bi 结尾的 LIS 的长度一样,而且b序列的字典序要最小
分析:基于用二分求 LIS 的方法,后来的元素不影响先进的元素的LIS的长度,每次读入一个 ai,在装 a 的数组中二分找到不大于此 ai 的最小值的位置 p
新构造的 b 序列的对应值 bi 要和它有同样的 LIS 长度,就应该处在装 b 的数组的同一个位置 p 上
如果 b 数组的这个位置有值,就直接输出,如果是末尾,就要拓展一个新值
如果新进的 ai 能替换掉原来 a 数组中的某个值而不用拓展 a 数组的长度,那 bi 也应该能在 b 数组中找到。b 数组应该是随着 a 数组的拓展而拓展的
#include <stdio.h>
#define SIZE 100000
int a[SIZE],b[SIZE];
int find(int x, int end)
{
int left = 0,right = end-1;
while( left <= right )
{
int mid = left + right >> 1;
if( a[mid] < x )
left = mid + 1;
else
right = mid - 1;
}
return left;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int i,n;
int lena,lenb; // a,b数组的长度
int big; // 表示下一次b数组拓展时要用到的值
scanf("%d",&n);
for( i=lena=lenb=0, big=1; i<n; i++)
{
int in; // 新进的ai
int p; // ai应在的位置
scanf("%d",&in);
p = find(in,lena);
if( p == lena ) // a序列“不够装”,要拓展
a[lena++] = in;
else
a[p] = in; // 可以替换原序列的某个值
if( p == lenb )
printf("%d%c", b[lenb++]=big++, i+1==n?'\n':' ');
else
printf("%d%c", b[p], i+1==n?'\n':' ');
}
}
return 0;
}
因为 big 总是从1开始以1为步长递增,所以装在 b 数组里的肯定是1,2,…,lenb这样的值,所以其实不用 b 数组,直接用下标,但是要+1
… …
if( p == lenb )
printf("%d%c", lenb++ +1, i+1==n?'\n':' ');
else
printf("%d%c", p+1, i+1==n?'\n':' ');
… …