洛谷P10287 [GESP样题 七级] 最长不下降子序列

题目

传送门

题意

给定一个n 个节点 m 条边的有向无环图,其中节点的编号为1到n。对于编号为i的节点,其权值为a[i]。对于图中的一条路径,根据路径上的经过节点的先后顺序可以得到一个节点权值的序列,求图中所有可能序列中最长不下降子序列最大长度。

思路

有向无环图

一眼拓扑排序,没什么好说的

最长不下降子序列的最大长度

一眼动态规划

动态规划实现

定义状态

我们可以定义dp[i]为以i为结尾的最长不下降子序列的最大长度,但是我们发现会TLE。这时我们观察到了数据范围1<=a[i]<=10,这不是可以成为我们状态的第二维吗?于是我们的状态为dp[i][j]是以i为结尾的权值为j的最长不下降子序列的最大长度。

转移方程

我们可以这里利用了两个转移

  • 第一,我们不选择a[i],那么我们的转移方程为dp[i][j]=dp[i-1][j]
  • 第二,我们选择了a[i],But我们的前提条件为a[i]>=j,那么我们就可以利用我们的二维,只用遍历到j即可,转移方程为dp[i][j]=max(dp[i][j],dp[v][j]+1),这里的v指的是我们的边的前驱

初始化

我们直接dp[i][a[i]]=1即可

拓扑排序的用途

我们每次遍历入度为0的点时,便加入我们的队列即可,每次取出我们的队头时遍历他能去的边,然后把他遍历到的边的入度减一即可,如果为0就入队

代码

高清有码(有注释)

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;//定义常量是个好习惯,复制你数错0 
int n,m;//看题目解释 
int dp[N][15],a[N],ru[N];//看题目解释,ru[i]是每个点的入度数 
vector<int>v[N];//建图 
int main(){
	std::ios::sync_with_stdio(false);//关闭输入输出流 
	std::cin.tie(0);
	std::cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	for(int i=1;i<=m;i++){
		int u1,v1;
		cin>>u1>>v1;
		v[u1].push_back(v1);//建图 
		ru[v1]++; //入度++ 
	}
	queue<int>q;
	for(int i=1;i<=n;i++){
		if(ru[i]==0){
			q.push(i);//寻找入度为0的点,放入队列 
		} 
		dp[i][a[i]]=1;//初始化 
	}
	while(!q.empty()){
		int u=q.front();
		q.pop();
		for(int i=0;i<v[u].size();i++){//遍历能够到达的边 
			ru[v[u][i]]--;//入度-- 
			if(ru[v[u][i]]==0){
				q.push(v[u][i]);//入队列 
			}
			for(int j=1;j<=a[v[u][i]];j++){//只用遍历小于a[i]的 
				dp[v[u][i]][a[v[u][i]]]=max(dp[v[u][i]][a[v[u][i]]],dp[u][j]+1);//转移方程2	,取a[i] 
			}
			for(int j=1;j<=10;j++){
				dp[v[u][i]][j]=max(dp[v[u][i]][j],dp[u][j]);//转移方程1 ,不取a[i] 
			}
		}
	}
	int ans=0;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=10;j++){
			ans=max(ans,dp[i][j]);//遍历寻找最大值 
		} 
	}
	cout<<ans;//输出 
	return 0;
} 

高清无码(无注释)

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,m;
int dp[N][15],a[N],ru[N];
vector<int>v[N];
int main(){
	std::ios::sync_with_stdio(false);
	std::cin.tie(0);
	std::cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	for(int i=1;i<=m;i++){
		int u1,v1;
		cin>>u1>>v1;
		v[u1].push_back(v1);
		ru[v1]++; 
	}
	queue<int>q;
	for(int i=1;i<=n;i++){
		if(ru[i]==0){
			q.push(i);
		} 
		dp[i][a[i]]=1;
	}
	while(!q.empty()){
		int u=q.front();
		q.pop();
		for(int i=0;i<v[u].size();i++){
			ru[v[u][i]]--;
			if(ru[v[u][i]]==0){
				q.push(v[u][i]);
			}
			for(int j=1;j<=a[v[u][i]];j++){
				dp[v[u][i]][a[v[u][i]]]=max(dp[v[u][i]][a[v[u][i]]],dp[u][j]+1);	
			}
			for(int j=1;j<=10;j++){
				dp[v[u][i]][j]=max(dp[v[u][i]][j],dp[u][j]);
			}
		}
	}
	int ans=0;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=10;j++){
			ans=max(ans,dp[i][j]);
		} 
	}
	cout<<ans;
	return 0;
} 

代码还需自己打,心急则味散QWQ

感悟

转眼我就高一了,但是我只有CSP-J一等的实力,你说我还能拿NOI金牌吗?

空悲切

posted @ 2026-08-21 15:13  陈晨2011  阅读(4)  评论(0)    收藏  举报