牛客3007C-汉诺塔-最长下降子序列+贪心模拟

链接:https://ac.nowcoder.com/acm/contest/3007/C
来源:牛客网

题目描述:

现在你有 N 块矩形木板,第 i 块木板的尺寸是 Xi*Yi,你想用这些木板来玩汉诺塔的游戏。
我们知道玩汉诺塔游戏需要把若干木板按照上小下大的顺序堆叠在一起,但因为木板是矩形,所以有一个问题:
第 i 块木板能放在第 j 块木板上方当且仅当 Xi<Xj 且 Yi<Yj,于是你很可能没法把所有的木板按照一定的次序叠放起来。
你想把这些木板分为尽可能少的组,使得每组内的木板都能按照一定的次序叠放。
你需要给出任意一种合理的分组方案。
提醒:“任意”意味着你的答案不必和标准输出完全一致,只要正确即可。

输入描述:

第一行,一个正整数 N
接下来 N 行,每行两个正整数表示 Xi 和 Yi
对于所有的数据,1≤N≤100,000,1≤Xi,Yi≤N,Xi 互不相等且 Yi 互不相等

输出描述:

输出文件包含两行,第一行一个正整数,表示最少组数
第二行 N 个正整数,依次表示你的方案中每块木板分在了哪一组
组的编号必须是从 1 开始的连续整数

输入样例:

3
1 1
2 3
3 2

输出样例:

2
1 1 2

核心思想:

数据存在结构体h[i]中,h[i].pos表示木板编号。

先按x升序排列,问题转换成数组y至少要分成多少个上升子序列,设其个数为k。
则k=数组y的最长下降子序列长度。

贪心模拟分组过程:
每个分组内部必须是单调递增的。
a[i]记录第i个分组当前最大的数,下一个y分组的贪心规则如下:
1、a[i]小于y
2、a[i]尽可能的大(因为y要覆盖a[i],a[i]尽可能的大,把小的a[i]留给后续的数匹配)

详见代码。

代码如下:

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=1e5+20;
int a[N],cnt,b[N];//b[i]表示第i个木板所在组号 
struct node{
	int x,y,pos;
	bool operator <(const node &oth)const
	{
		return x<oth.x;
	}
}h[N];
int fun(int x)//二分查找最大的小于x的数的位置 
{
	int l=1,r=cnt-1,ans=0;
	while(l<=r)
	{
		int mid=(l+r)>>1;
		if(a[mid]<=x)
		{
			ans=mid;
			r=mid-1;
		}
		else
			l=mid+1;
	}
	return ans;
}
int main()
{
	cnt=1;
	int n;
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d",&h[i].x,&h[i].y);
		h[i].pos=i;
	}
	sort(h+1,h+n+1);//按x排序 
	for(int i=1;i<=n;i++)
	{
		int z=fun(h[i].y);
		if(!z)//没有比h[i].y小的数,新建一个塔 
		{
			b[h[i].pos]=cnt;
			a[cnt]=h[i].y;
			cnt++;//新建,数目+1 
		}
		else//第h[i].pos个盘子,挂在第z个塔上 
		{
			b[h[i].pos]=z;
			a[z]=h[i].y;
		}
	}
	printf("%d\n",cnt-1);
	for(int i=1;i<n;i++)
		printf("%d ",b[i]);
	printf("%d\n",b[n]);
	return 0;
}
发布了147 篇原创文章 · 获赞 135 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Nothing_but_Fight/article/details/104333638
今日推荐