题面
题意
输入n对数,选择尽可能多对,使任意两对中其中一对的两数比另外一对.
思路
按第一个数的大小排列,找出另外一个数的最长不下降子序列
错误方法(TLE)
用dp思想,仅讨论当前数选择或不选择,选择结果,之前比它小的数的结果+1.
复杂度为O(n^2)
代码
#include<bits/stdc++.h>
#define N 100100
using namespace std;
struct Pea
{
int ml,ll,bh;
}pea[N];
int n,dp[N],len,ans[N],da[N],dd,head;
bool cmp(Pea u,Pea v)
{
return u.ml<v.ml;
}
int dfs(int now)
{
if(dp[now]!=-1) return dp[now];
int i,j,res=1,k,an;
for(i=1;i<now;i++)
{
an=dfs(i);
if(pea[i].ll<pea[now].ll&&pea[i].ml!=pea[now].ml&&an+1>res)
{
k=i;
res=an+1;
}
}
dp[now]=res;
if(res!=1) ans[now]=k;
if(res>len)
{
len=res;
head=now;
}
return res;
}
int main()
{
int i,j;
cin>>n;
for(i=1;i<=n;i++)
{
scanf("%d%d",&pea[i].ml,&pea[i].ll);
pea[i].bh=i;
}
sort(pea+1,pea+n+1,cmp);
memset(dp,-1,sizeof(dp));
memset(ans,-1,sizeof(ans));
dfs(n);
/*
for(i=1;i<=n;i++)
{
cout<<dp[i]<<" ";
}
//*/
while(head!=-1)
{
dd++;
da[dd]=pea[head].bh;
head=ans[head];
}
sort(da+1,da+len+1);
printf("%d\n",len);
for(i=1;i<=len;i++)
{
printf("%d ",da[i]);
}
}
正解
排序时,在第一个数从小到大的前提下,让第二个数从大到小,即在第一个数相等的情况下,第二个数较大的排在前面(方便后面的更新),之后用一个数组(dp[i]=k)记录长度为i时的最大数为k,为了让子序列尽可能的长,自然希望i相等时,k尽量小,故根据上述的排序方法,每一次均可以更新k,然后用一个数组记录每一个数之前的那个数(具体见代码).
每次更新时,都用lower_bound找到数组中恰好大于它的数,并且以此更新.
复杂度为O(nlogn).
代码
#include<bits/stdc++.h>
#define N 100100
#define M 100000000
using namespace std;
struct Pea
{
int ml,ll,bh;
}peo[N];
int n,len,ans[N],da[N],dd,head,wz,dp[N],pos[N];
bool cmp(const Pea& u,const Pea& v)
{
return u.ml<v.ml||u.ml==v.ml&&u.ll>v.ll;
}
int main()
{
int i,j;
cin>>n;
for(i=1;i<=n;i++)
{
scanf("%d%d",&peo[i].ml,&peo[i].ll);
peo[i].bh=i;
}
sort(peo+1,peo+n+1,cmp);
for(i=1;i<=n;i++)
{
wz=lower_bound(dp+1,dp+len+1,peo[i].ll)-dp;
if(wz==1)
{
ans[i]=-1;
}
else
{
ans[i]=pos[wz-1];
}
pos[wz]=i;
dp[wz]=peo[i].ll;
len=max(len,wz);
}
printf("%d\n",len);
head=pos[len];
while(head!=-1)
{
dd++;
da[dd]=peo[head].bh;
head=ans[head];
}
sort(da+1,da+dd+1);
for(i=1;i<=dd;i++)
{
printf("%d ",da[i]);
}
}