SGU - 199(最长不下降子序列)

190 篇文章 2 订阅

题面

题意

输入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]);
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值