蓝桥杯23年第十四届省赛-接龙数列|DFS、线性DP

本文介绍了如何使用深度优先搜索(DFS)和优化后的动态规划(DP)解决蓝桥杯竞赛中的接龙数列问题。DFS用于遍历序列并记录最大可能的连续子序列长度,而优化的DP通过仅关注当前字符的前缀子序列来提高效率。
摘要由CSDN通过智能技术生成

题目链接:

1.接龙数列 - 蓝桥云课 (lanqiao.cn)

蓝桥杯2023年第十四届省赛真题-接龙数列 - C语言网 (dotcpp.com)

说明:

 dfs要注意,不能连上的时候绝对不选;能连上的时候可以选,也可以不选。不选的dfs分支是必须经过的过程。不是 只有不能连上才不选。

DFS代码:

#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
const int N=1e5+10;
int ans=0;
int mx=0;int n;
vector<string> s;//sequence

void dfs(int k,int cnt,char c){
	
	
	if(cnt+n-k<mx) return;
	
	if(k>=n){
		if(cnt>mx){
		mx=cnt;
	}
	return;
	}
	
	if(c==' '){
		c=s[k].at(s[k].size()-1);
		dfs(k+1,1,c);
		
		dfs(k+1,0,' ');
	}
	else{
	char mc=s[k].at(s[k].size()-1);
	
	if(c==s[k].at(0))
	dfs(k+1,cnt+1,mc);

	//else 不能写else,不能连上的时候绝对不选;能连上的时候可以选,也可以不选。
  //所以 这个不选的dfs 是必须经过的过程
	dfs(k+1,cnt,c);
	}
	
	
	
} 

signed main() {
 
   ios::sync_with_stdio(0);
   cin.tie(0);
   cout.tie(0);
   
   cin>>n;
   for(int i=0;i<n;i++){
   	string s1;
   	cin>>s1;
  	s.push_back(s1);
   }
   
   dfs(1,1,s[0].at(s[0].size()-1));
   
   if(mx<n-1)
   dfs(1,0,' ');
   
   
   cout<<n-mx;
	
	
  return 0;
}





普通DP:容易想到构造以第i项为结尾的dp数组

dp[i]表示以第i项结尾的最长数列长度,状态转移方程为

dp[i]=max{dp[j]},0<j<=i,且第j项的末尾等于第k项的头部(即前i-1项所有末尾与第i项头部相同的选一个最大的)。

#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
const int N=1e5+10;
//最小的数列长度为1,不能设为0,比如长度为1时,设为0就有问题
int mx=1;
int n;
vector<string> s;//sequence
int dp[N];

signed main() {
 
   ios::sync_with_stdio(0);
   cin.tie(0);
   cout.tie(0);
   
   s.push_back(" ");
   cin>>n;
   for(int i=0;i<n;i++){
   	string s1;
   	cin>>s1;
  	s.push_back(s1);
   }
   
   dp[1]=1;
 //  mx=max(mx,dp[1]);
   for(int i=2;i<=n;i++){
   	dp[i]=1;
   	for(int j=1;j<i;j++){
   		if(s[i].at(0)==s[j].at(s[j].size()-1))
   	dp[i]=max(dp[i],1+dp[j]);	
	   }
   mx=max(mx,dp[i]);
   }
	
	
  cout<<n-mx;
  return 0;
}





优化后的DP:

因为需要选出最大的长度,dp[i]只与前i-1项中以它的首字母结尾的最长数列有关 ,所以每次保存尾部字符对应的最长序列长度是多少,这样计算dp[i]时可以直接找到以它的首字母为结尾的最长数列,就从O(n) 降到了O(1) 。

关键在于想到把一个数结尾的元素作为动态规划的递推式子的一部分(比如dp[i]的i)来考虑

代码:

#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
const int N=1e5+10;
//最小的数列长度为1,不能设为0,比如长度为1时,设为0就有问题
int mx=1;
int n;
vector<string> s;//sequence
int dp[N];
int r[N];//尾部字符对应的最长序列长度

signed main() {
 
   ios::sync_with_stdio(0);
   cin.tie(0);
   cout.tie(0);
   
   s.push_back(" ");
   cin>>n;
   for(int i=0;i<n;i++){
   	string s1;
   	cin>>s1;
  	s.push_back(s1);
   }
   
   dp[1]=1;
   r[s[1].at(s[1].size()-1)-'0']=dp[1];
   
   for(int i=2;i<=n;i++){
   	dp[i]=1;	
   dp[i]=max(dp[i],1+r[s[i].at(0)-'0']);
   r[s[i].at(s[i].size()-1)-'0']=max(dp[i], r[s[i].at(s[i].size()-1)-'0']) ; 
   
   mx=max(mx,dp[i]);
   }
	
  cout<<n-mx;
  return 0;
}

  • 24
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

guts350

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值