Problem-977F-Codeforces(dp)

cf的第一场div3!

并没有熬夜去打。因为想摸清大概难度。下午信息课时直接看了F题。

题目链接:http://codeforces.com/problemset/problem/977/F

看到标题就知道是一道最长上升子序列题。立马想到要用dp,然后立即着手去写。

但是测样例是却发现出现了问题,仔细一看才发现我忽略了一个重要的条件:序列是连续的。

然后很快改进代码。提交。

#include<bits/stdc++.h>
using namespace std;

int n;
int a[200002];
int dp[200002],last[200002];
int tmp;

void solve(int n){
	if (n==-1) return;
	solve(last[n]);
	printf("%d ",n+1);
	return;
}

int main(){
	scanf("%d",&n);
	for (int i=0;i<n;i++) scanf("%d",&a[i]);
	memset(last,-1,sizeof(last));
	for (int i=0;i<n;i++) dp[i]=1;
	for (int i=1;i<n;i++) 
		for (int j=0;j<i;j++) if (a[j]==a[i]-1) {
			if (dp[j]+1>dp[i]){
				dp[i]=dp[j]+1;
				last[i]=j;
			}
		}
	int ans=0,Ans=0;
	for (int i=0;i<n;i++) {
		if (dp[i]>ans) {
			ans=dp[i];
			Ans=i;
		}
	}
	printf("%d\n",ans);
	solve(Ans);
	return 0;
}

没错,然后我就t5了。

看了下数据范围:2*10^5,O(n^2)的时间复杂度确实肯定要tle

但是想了半天没想到优化的方法?因为上课老师讲过最长上升子序列的dp解法,没有其他优化的方法了啊?

这是突然想到,这道题的独特之处:序列需连续

那么解法就显而易见了:dp[i]代表以i结尾的序列的最大长度。从前往后刷新。这样只需一个循环。状态转移方程是dp[i]=max(dp[i],dp[i-1]+1)O(n)的复杂度,2s的时间限制绰绰有余。

然后就可以找到dp[i]的最大值即最长长度。然后从初始序列的最后开始,从最长上升子序列的最后一个数字开始找其相应的位置,每找到一个,期望值-1(贪心:从最后往前找必定是最优解)PS感觉表述很不清楚但是看了代码应该就懂了?

但还需注意:dp不能直接开数组!如果直接写int dp[1000000000]会报错!!!因为数组太大了!!!所以这是就需要用map。10^9 int已经足够。

AC代码如下:

#include<bits/stdc++.h>
using namespace std;

int n;
int a[200002];
int cnt=0,last;
vector<int> ans;
map <int,int> dp;

int main(){
	scanf("%d",&n);
	for (int i=0;i<n;i++) scanf("%d",&a[i]);
	dp[a[0]]=1;
	for (int i=1;i<n;i++) 
		if (dp[a[i]-1]+1>dp[a[i]])
			dp[a[i]]=dp[a[i]-1]+1;
	for (int i=0;i<n;i++) {
		if (dp[a[i]]>cnt) {
			cnt=dp[a[i]];
			last=a[i];
		}
	}
	printf("%d\n",cnt);
	for (int i=n-1;i>=0;i--) {
		if (a[i]==last){
			ans.push_back(i+1);
			last--;
		}
	}
	for (int i=ans.size()-1;i>=0;i--) printf("%d ",ans[i]);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_42158832/article/details/80232606