【I - 最少拦截系统】

80 篇文章 0 订阅
80 篇文章 0 订阅

思路:

一:
  • 这是一道经典的动归题目,经典在它可以牵引出 Dilworth定理、LIS(Longest Increasing Subsequence)

  • 先说 DilworthDilworth定理包含如下知识:偏序集、链、反链Dilworth 包括两个对偶定理:

    1. 最大长度 = 最少反链划分数
    2. 最大反链长度 = 最少划分数
  • 那如何使用该定理呢?非常简单。以本题为例:求最少不上升(>=)划分数。首先:链和反链如何定义并不需要很深的理解,我们只需要定义一种关系,在本题中,此关系为:既前且高(=),时间上不会有重叠,高度上相当于 “高于等于”;而最少划分最大长度是紧密绑定不能更改的。如此,本题即可等价于求最大上升(<)反链长度。十分对偶。

  • Dilworth定理 实体化为公式:
    设某种关系为 &,它的反关系为 %(例如 >=<,注意严格不严格的对偶)

    1. 具有&关系链 的最少划分,等于求 具有%关系链 的最大长度。
    2. 具有%关系链 的最少划分,等于求 具有&关系链 的最大长度。
  • 通过 Dilworth 公式,我们可以轻易发现本题的一种解法:直接求出所给序列的 LIS。

二:
  • 又可根据一结论:暴力具有某关系的子链,找到子链的数目即为最少的该关系的链划分数。根据本结论,可以给出本题的第二种解法。
三:
  • 我们已经知道,第一种解法(Dilworth)实质上需要求的就是 LIS。那么 LIS 又有几种解法呢?
    1. N^2:三角形动态规划。
    2. NlgN:用到二分、顶替的思想。详见再见~雨泉的BLOG。我的代码中采用了非常非常简单的,使用了STL的二分求 LIS的方法,来自BIGKAKA的BLOG感谢。

代码:

  • 三角形朴素动归,求LIS:46ms 1428kB
//46ms		1428kB


#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int maxn = 5005;

int N;
int H[maxn];
int dp[maxn];

int main(){
	while(cin>>N){
		for(int i=1;i<=N;i++)
			dp[i] = 1;
		int MAX = 0;
		for(int i=1;i<=N;i++)
			scanf("%d" , H + i);
		for(int i=1;i<=N;i++)
			for(int j=1;j<i;j++)
				if(H[i] > H[j])
					dp[i] = max(dp[i] , dp[j] + 1);
		for(int i=1;i<=N;i++)
			MAX = max(dp[i] , MAX);
		cout<<MAX<<endl;
	}
	return 0;
}
  • 特殊结论:0ms 1424kB
//0ms		1424kB


#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define INF 0x3f3f3f3f

using namespace std;

const int maxn = 5005;

int N;
int H[maxn];
bool vis[maxn];
int ans;

void INIT(){
	ans = 0;
	memset(vis , 0 , sizeof(vis));
	return ;
}

int main(){
	while(cin>>N){
		INIT();
		for(int i=1;i<=N;i++)
			scanf("%d" , H + i);
		while(true){
			int MAX = INF;
			for(int i=1;i<=N;i++){
				if(!vis[i] && MAX >= H[i]){
					MAX = H[i];
					vis[i] = true;
				}
			}
			if(MAX == INF)
				break;
			ans++;
		}
		cout<<ans<<endl;
	}
	return 0;
}
  • LIS的NlgN:15ms 1444kB
//15ms		1444kB


#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define INF 0x3f3f3f3f

using namespace std;

const int maxn = 5005;

int N;
int H[maxn];
int dp[maxn];

void INIT(){
	memset(dp , INF , sizeof(dp));
	return ;
}

int main(){
	while(cin>>N){
		INIT();
		for(int i=1;i<=N;i++)
			scanf("%d" , H + i);
		for(int i=1;i<=N;i++)
			*lower_bound(dp , dp+N , H[i]) = H[i];
		cout<<lower_bound(dp , dp+N , INF) - dp<<endl;
	}
	return 0;
}

二刷:

  • N^2:46ms 1416kB
//46ms		1416kB


#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

const int maxn = 30005;

int N;
int w[maxn];
int dp[maxn];

int main(){
	while(cin>>N){
		for(int i=1;i<=N;i++)
			cin>>w[i];
		int MAX = 0;
		for(int i=1;i<=N;i++){
			dp[i] = 1;
			for(int j=1;j<=i;j++)
				if(w[j] < w[i])
					dp[i] = max(dp[i] , dp[j] + 1);
			MAX = max(MAX , dp[i]);
		}
		cout<<MAX<<endl;
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值