Consecutive Subsequence - 每天一把CF - 20201031

博客讲述了使用动态规划解决Codeforces题目977F中寻找最长连续子序列的问题。作者经历了从TLE到AC的过程,重点讨论了如何优化状态转移,通过建立映射数值和长度的哈希表降低复杂度,最终实现AC。
摘要由CSDN通过智能技术生成

2020-10-31

dp

977F 1700

题目

原题链接:https://codeforces.com/problemset/problem/977/F

在这里插入图片描述

思路

题目大意:给定一个整数数组a,在里面找一个最长的子序列,子序列满足每一项都比且只比前一项大1,即[x,x+1,…,x+k−1]。要求给出最长子序列的长度以及对应的子序列

思路:这道题我做的真的是一波三折折折折折折。

首先看到题就应该知道这道题是求最长上升子序列。

然后我直接打了个模板,在状态转换的时候用了两个for去做,如下

for(int i = 1; i <= n; i++)
	for(int j = i-1;j ; j--){
		........
	}

然后直接第8个用例TLE

然后我又改成用map数组去存储每个数在原数组中的索引,如下

map<int, vector<int>> mp;

a[i] = read();
mp[a[i]].push_back(i);

if (mp.find(tp) != mp.end()) {
				int len = mp[tp].size();
				for (int j = len - 1; j >= 0; j--) {
					if (mp[tp][j] < i) {
						dp[i] = dp[mp[tp][j]] + 1;
						pre[i] = mp[tp][j];
						goto OUT;
					}
				}
			}

这个一开始很顺利,但还是在第33个样例再次TLE,数据量是2e5,之前的2e5都过了,这个用例又来卡人了。

200000
1 2 1 2 1 2 1 2 1 2 .......................//就是这样无限重复下去

然后,无计可施之下只能加了个快读,依旧没卵用,卡的就是状态转换的那一段。

最后实在没得法了,只能开启面向百度编程大法,哈哈哈哈哈哈哈哈哈。

然后发现本题的重点是看出前一个状态是确定的,直接map做!

就是去考虑 : 当我们现在拿到一个数a时,我们一定是去a找前面第一个a-1的时候的值,也只跟这个a-1的位置有关。

所以我们直接拿map做就好了,映射数值和长度,即拿到数a,那我们求的状态就是dp[a],然后我们直接找dp[a-1]

即可,因为是字典哈希,这个拿值是O1的复杂度,至此终于将状态转换的部分的复杂度降下来了。

这几题都是在回来的火车上敲的,同车的大佬还跟我讲了一种最长上升子序列nlogn的做法,可以去洛谷搜一下导弹拦截那道题。这里用不着那么做就不展开了。

代码实现

TLE款

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <map>
#include <vector>
#include <string>
using namespace std;

#define mes0(c) memset((c),0,sizeof(c))
#define fsios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define rep(n) for(int i=1;i<=(n);i++)
#define inp(a, n) for (int i = 1; i <= (n); i++) cin >> a[i];
#define ll long long
#define fi first
#define se second

const ll MAX = 2e5 + 5;
const ll ifi = 1e6 + 5;

int n;
int a[MAX], pre[MAX];
int dp[MAX], ans[MAX];
map<int, vector<int>> mp;


namespace IO {
   
	inline  int  read() {
   
		int x <
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值