汕头市队赛SRM07

A 你的麻将会排序吗 SRM 07

曾经有过一些沉迷日麻的小孩纸,后来呀,他们都去寻找自己的世界了。

        kpm也是这样的小孩纸。他想有一只自动整理牌的机器。当麻将以给定的顺序进入机器时,通过机器的运转,使得麻将们出机器的顺序是递增的。所以kpm需要在机器中建立一些传送带 (假设这些传送带都是足够长,可以停放很多很多的麻将),问题是,现在kpm需要建立多少条传送带才能完成他的机器。

        kpm大概有10^5块麻将吧。

输入格式

               第一行是一个不大于10^5的数,表述麻将的总数。

               第二行是麻将依次进入机器的编号,ai表示编号为ai的麻将在i时刻进入机器,保证是一个1-n的排列。

输入格式

               一个数字,表示这个机器最少需要建几条传送带。

样例输入

9
8 4 2 5 3 9 1 6 7

样例输出

4

我刚开始没看懂题意

问了一下才明白

就是传送带上的麻将你可以控制它什么时候出去(维持一条传送带上的相对顺序)

然后题意就显然了

求多条互不相交的下降序列的最少条数



这题与noip1999提高组导弹拦截的第二问十分相似

大家可以参照对比一下 :

题目描述

某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。

输入格式

输入数据为两行,

第一行为导弹的数目N(n<=1000)

第二行导弹依次飞来的高度,所有高度值均为不大于30000的正整数。

输出格式

输出只有一行是这套系统最多能拦截的导弹数和要拦截所有导弹最少要配备这种导弹拦截系统的套数。两个数据之间用一个空格隔开. 

样例输入

8
389 207 155 300 299 170 158 65

样例输出

6 2

对于这类问题很容易想到每次求最长非升子序列,然后把这些元素删除。再求一次最长非升子序列,然后把这些元素删除。如此重复,直到全部删除。但是这里局部最优不是整体最优。

 举个反例:6 1 7 3 2  错解:6 3 2/1/7  正解:6 1/7 3 2

有一种方法是最小链覆盖=最长反链

即换个角度考虑 1.所有导弹都要打下来 2.必须是不严格的单调递减 结论:只要把最长上升子序列都打下来了,所有导弹就可以打下来。那么问题就抽象成了求最长上升子序列

而求最长上升子序列是n方的

显然不满足本题的要求

所以另一种解法是

我们会发现每一条下降序列对别的未放入的点有影响的只有最后一个元素,所以我们只需存储最后一个元素即可

当我们一个一个数来处理时

会发现

对于每一个数,假设前方已经有一些下降序列了

根据贪心策略

我们应该选队首元素比它大的(下降)里面最小的(贪心)(即为后缀)

而当没有能放进去的序列时

我们就新开一个(放在最后)

容易发现序列的末端也是具有单调性的(每条序列的末端)

所以我们可以二分查找

这样复杂度就为nlogn



再谈一些对找后缀和二分的理解吧
找后缀如果要log级别的话很明显是建立在排好序的基础上的
而排好序了就可以二分嘛
二叉查找树也是基于二分的思想
但如果只是为了这道题去写平衡树就太不值了
可以直接手写二分或者用STL的set(还不会(逃
平衡树的优势在于可以插入删除啊区间操作啊等等
发现set很好用啊
要去学(逃

#include<cstdio>
#include<cstring>
int cnt=0;
int w[100005];
int find(int k)
{
	int l=1,r=cnt;
	while(l<=r)
	{
		int mid=(l+r)>>1;
		if(w[mid]<k)	l=mid+1;
		else r=mid-1;
	}
	return l;
}
int main()
{
	int n,k;
	scanf("%d",&n);
	scanf("%d",&w[++cnt]);
	for(int i=2;i<=n;i++)
	{
		scanf("%d",&k);
		int s=find(k);
		if(s>cnt)	w[++cnt]=k;
		else w[s]=k;
	}
	printf("%d
",cnt);
	return 0;
}


set版本:

#include<cstdio>
#include<set>
int a[100010];
std::set<int>s;
std::set<int>::iterator it;
int main()
{
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)	scanf("%d",&a[i]);
	int ans=1;	s.insert(a[1]);
	for(int i=2;i<=n;i++)
	{
		it=s.lower_bound(a[i]);
		if(it==s.end())	ans++;
		else s.erase(it);
		s.insert(a[i]);
	}
	printf("%d
",ans);
	return 0;
}




B 好玩的麻将 SRM 07

背景&&描述

    天才麻将少女KPM立志要在日麻界闯出一番名堂。
    KPM上周又打了n场麻将,又控了分使得自己的排名是1..n的一个排列。
    但她这次想让妹子相信,闭关之后她的水平稳定上升。
    KPM出卖灵魂之后,膜法使答应用自己操控时间的能力帮助她。
    膜法使每次操作可以把某场比赛提到最前面或者最后面。
    膜法使急着去分享人生经验,问你最少需要多少次操作可以使KPM的排名单调递增。

输入格式

第一行一个整数n,第二行n个整数,表示n场比赛的排名。

输出格式

一个整数,表示最少需要操作多少次。

样例输入

4

1 4 3 2

样例输出

2

数据范围与约定

  • 对于100%的数据:1leq nleq 10^5

这道题其实也不难想(然而我并没有想出来)

我们的思路是先“固定”一些数,然后对其他的数进行操作

会发现对于不连续的数

比如1,3

中间是要插入一个2的

而这个2是不能直接插进去的

所以我们就要找连续的数

比如1,2

这时我们就可以通过把3移至序列末端来实现1,2,3的顺序

然而。。。。。。

哇哇哇哇哇

这种边跑然后不符合条件时取最大值的要记得跑完时再取一次啊

又因为这个WA了一次(哭唧唧。。

#include<cstdio>
#include<cstring>
int pl[100005];
int main()
{
	int n,k;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)	scanf("%d",&k),pl[k]=i;
	int max=0;int sum=1;
	for(int i=2;i<=n;i++)
	{
		if(pl[i]>pl[i-1])	sum++;
		else
		{
			max=max>sum?max:sum;
			sum=1;
		}
	}
	max=max>sum?max:sum;
	printf("%d
",n-max);
	return 0;
}








原文地址:https://www.cnblogs.com/Brian551/p/7353007.html