【动态规划+二分】奥运大包围

题目描述

题目描述:
为了迎接奥运,市体育局举行手拉手大包围活动,开始时N个人手拉手围成一个圈。后来这些人中的一些按顺序向里面出圈形成一个新圈。从而使原圈形成一个从高到低,最低与最高连接的圈。新圈重复相同的操作,直到没有人要出圈为止。问最少要形成多少个这样的圈。

输入格式

输入:第一行N个人,第二行输入N个人的身高(每个身高中用空格隔开)N<=1000

输出格式

输出:最少形成多少个这样的圈。

样例输入

样例输出


很容易看出和导弹拦截的第二问是一样的模型。。只是多了环

只是我笔算验证的时候很是不确定,因为样例都画不出来。。然后,猥琐地瞟了一眼讨论,大家都是这样想的,于是乎就打下去了

是那一个定理,下降子序列的个数等于最长不降子序列的长度。

但是由于有环,因此要枚举断点,每次都要把s数组初始化(高度是无限高),s[1]要设成第一个点然后从第二个点开始递推,不然没法递推下去


提交三次:

1、超时,朴素O(n^3)

2、超时,加了几个系数优化,无用。

3、AC,改了二分,因为在同一个大区间内,一个较短的下降序列和一个较长的下降序列,(我们只记录下序列中最高的那个最低的一个序列),前者中最高的必定比后者最高的矮!因此对于可行性有单调性。


应该说现在二分实现上比较熟了吧,只是可能有些建模,要找出单调性,不知道能力如何


#include <cstdio>
#include <string>
#include <cstring>

inline long getint()
{
	long rs=0;bool sgn=1;char tmp;
	do tmp = getchar();
	while (!isdigit(tmp)&&tmp-'-');
	if (tmp=='-'){tmp=getchar();sgn=0;}
	do rs=(rs<<3)+(rs<<1)+tmp-'0';
	while (isdigit(tmp=getchar()));
	return sgn?rs:-rs;
}

long h[1010];
long s[1010];
#define MAX(a,b) ((a)>(b)?(a):(b))
#define MIN(a,b) ((a)<(b)?(a):(b))

long maxs = 0;
long height = 0;
long find()
{
	long l = 0;
	long r = maxs;

	long out = 0;
	while (l <= r)
	{
		long mid = (l+r)>>1;
		if (h[s[mid]]<=height)
		{
			if (out < mid)
				out = mid;
			l = mid+1;
		}
		else
		{
			r = mid-1;
		}
	}
	return out;
}

int main()
{
	freopen("olympic.in","r",stdin);
	freopen("olympic2.out","w",stdout);
	long n = getint();
	for (long i=1;i<n+1;i++)
	{
		h[i] = getint();
		h[i+n] = h[i];

	}
	long ans = 0x7f7f7f7f;
	h[1005] = 0x7f7f7f7f;
	for (long k=1;k<n;k++)
	{
		for (long i=1;i<1005;i++)
			s[i] = 1005;
		maxs = 0;
		s[1] = k;
		maxs = 1;
		for (long i=k+1;i<k+n;i++)
		{
			height = h[i];
			long re = find();
			if (h[s[re+1]] > h[i])
				s[re+1] = i;
			maxs = MAX(maxs,re+1);
		}
		ans = MIN(ans,maxs);
	}
	printf("%ld",ans);
	return 0;
}

朴素:

#include <cstdio>
#include <string>
#include <cstring>

inline long getint()
{
	long rs=0;bool sgn=1;char tmp;
	do tmp = getchar();
	while (!isdigit(tmp)&&tmp-'-');
	if (tmp=='-'){tmp=getchar();sgn=0;}
	do rs=(rs<<3)+(rs<<1)+tmp-'0';
	while (isdigit(tmp=getchar()));
	return sgn?rs:-rs;
}

long h[1010];
long f[1010];
#define MAX(a,b) ((a)>(b)?(a):(b))
#define MIN(a,b) ((a)<(b)?(a):(b))
int main()
{
	freopen("olympic.in","r",stdin);
	freopen("olympic.out","w",stdout);
	long n = getint();
	for (long i=1;i<n+1;i++)
	{
		h[i] = getint();
		h[i+n] = h[i];
	}
	long ans = 0x7f7f7f7f;
	for (long k=1;k<n;k++)
	{
		long _ans = 0;
		for (long i=k;i<k+n;i++)
		{
			f[i] = 1;
		}
		for (long i=k;i<k+n;i++)
		{
			for (long j=k;j<i;j++)
			{
				if (h[i] >= h[j])
				{
					f[i] = MAX(f[i],f[j]+1);
					_ans = MAX(_ans,f[i]);
					if (_ans >= ans)
					{
						i = k+n+1;
						break;
					}
				}
			}
		}
		ans = MIN(ans,_ans);
	}
	printf("%ld",ans);
	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值