题目连接:http://acm.pku.edu.cn/JudgeOnline/problem?id=1631
写了三个版本,第一个版本到现在也没搞清楚哪里有问题
版本一:
1,这个问题就是要求线路的最大不相交子集中元素的个数,opt[i][j]代表左方前i条线在右方j个接线柱范围内的最优值
2,最优子结构是:opt[i][j] = opt[i-1][j](end[i]>j时)或者max(opt[i-1][j],opt[i-1][end[i]-1]+1)(end[i]<=j时)
3,边界条件是:opt[1][j] = 0(j<end[1]时)或者1(j>=end[1]时)
#include <iostream>
using namespace std;
#define MAX 5000
int end[MAX],size;
int t,c,p,i,j;
unsigned short opt[2][MAX];
int main()
{
freopen("in.txt","r",stdin);
cin >> t;
while(t--)
{
cin >> size;
for(i = 1;i <= size;++i)
cin >> end[i];
for(i = 0;i <= size;++i)
{
if(i < end[1])
opt[0][i] = 0;
else
opt[0][i] = 1;
}
for(i = 2;i <= size;++i)
{
for(j = 0;j <= size;++j)
{
c = (i+1) % 2;
p = (c == 0 ? 1 : 0);
if(j < end[i])
opt[c][j] = opt[p][j];
else
opt[c][j] = opt[p][j] > opt[p][end[i]-1] + 1?
opt[p][j] : opt[p][end[i]-1] + 1;
}
}
cout << opt[(size+1)%2][size] << endl;
}
return 0;
}
版本二:
1,nlogn版本的单调上升子序列算法
2,opt[i]代表以i元素结尾的最长上升子序列的长度,mm[i]代表所有长度为i的上升子序列中最小的末尾元素。
3,假设当前考察元素为end[i],它该加到谁的后面呢?若end[i]比前面的所有元素都小那么它应该自立门户,成为长度为1的子序列,此时应该看它是否比mm[1]中的元素小,若更小就更新mm[1]中的值;若end[i]比前面的所有元素都大那么它可以加到已知的最长上升序列的后面成为更长的子序列,此时mm的长度加1,存上end[i]的值;若end[i]不是这两种极端情况,该加到谁的后面?找到一个一存在的最长的上升子序列还要满足该子序列最后一个元素小于end[i],这些信息通过二分查找mm数组即可实现,方法就是让lf始终指向大于等于end[i]的元素,这样在循环跳出的时候lf指向了mm中第一个不小于end[i]的元素,换句话说lf左边第一个元素就是小于end[i]的元素,end[i]可以加到lf左边第一个元素的后面形成长度为lf的上升子序列,所以此时lf就是以end[i]结尾的最长上升子序列的长度,end[i]总是不大于lf当前所指向的元素,所以可以用end[i]更新lf处的元素。
#include <iostream>
using namespace std;
#define MAX 40000
int end[MAX],size;
int opt[MAX],mm[MAX];
int i,j,t,lf,rt,md,len;
int main()
{
freopen("in.txt","r",stdin);
cin >> t;
while(t--)
{
cin >> size;
for(i = 0;i < size;++i)
cin >> end[i];
len = 1;
opt[0] = 1;
mm[len] = end[0];
for(i = 1;i < size;++i)
{
lf = 1;
rt = len;
while(lf <= rt)
{
md = (lf+rt) / 2;
if(mm[md] < end[i])
lf = md + 1;
else
rt = md - 1;
}
if(lf > len)
len = lf;
opt[i] = lf;
mm[lf] = end[i];
}
cout << len << endl;
}
return 0;
}
版本三:
这里主要是考虑到了mm其实包含了长度信息,就不用再开opt数组了,节省了点内存!
#include <iostream>
using namespace std;
#define MAX 100
int end[MAX],size;
int opt[MAX],len;
int i,j,lf,md,rt,t;
int main()
{
freopen("in.txt","r",stdin);
cin >> t;
while(t--)
{
cin >> size;
for(i = 0;i < size;++i)
cin >> end[i];
opt[1] = end[0];
len = 1;
for(i = 1;i < size;++i)
{
lf = 1;
rt = len;
while(lf <= rt)
{
md = (lf+rt) / 2;
if(opt[md] < end[i])
lf = md + 1;
else
rt = md - 1;
}
//lf指向第一个大于或者等于end[i]的位置
if(len < lf)
len = lf;
opt[lf] = end[i];
}
cout << len << endl;
}
return 0;
}