去年我参加了javab组蓝桥杯拿到省二 感觉有点难度 (题目和c++的题目不太相同) 今天又重温了一下 c++的题目,准备今年继续战斗用c++去写 但是发现去年的题目比之前的题目难度上升了至少两个等级(用到了很多的dp或者是算法,暴力只能得到一点点分数)
今天刷真题的时候遇到了一个题目:0接龙数列
题目难度对于我这个小菜鸡来说值得写一下:
首先我们看题目
看完题目后我想的是直接看数据量(这是我的思路 有时候数据量可用提供给你很多信息 让你少走很多弯路 可用直接匹配到你想使用的算法)
一看我就知道这题不简单(才只是第五题 难度对于我来说感觉就挺大的)
那么数据量在20 的话肯定是暴力去做就行了(之前的题目暴力数据量都可以在60%以上)
但是我不想浪费这个时间去写 所以我就看了一下这个数据 一般在10^5的数据量肯定至少得nlogn的时间复杂度了 那么这个题目思路要怎么转变呢?
答案肯定是DP了
答案是要我们求 删除多少个数 ,那么我们是否可以求每个数能连接的最大数?
因为当前求的第一个字符都是用前面的数最后一个字符去连接
那么只需要将当前这个数的第一个字符遍历前面所有的数的最后一个字符 如果匹配 就加上前面的数:
最后算法代码:
DP暴力:
#include <iostream>
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
string arr[N];
int dp[N];
int main()
{
int n ;
cin>>n;
for(int i = 1;i<=n;i++){
cin>>arr[i];
dp[i] = 1;
}
for(int i = 1;i<=n;i++){
int temp = 0;
for(int j = 1;j<i;j++){
if(*arr[j].rbegin()== *arr[i].begin()){
temp = max(temp,dp[j]);
}
}
dp[i] +=temp;
}
int ans = 0;
for(int i = 1;i<=n;i++){
ans = max(ans,dp[i]);
}
cout<<n-ans;
// 请在此输入您的代码
return 0;
}
最后运行
可以通过10^4 那么肯定是超时了 O(n^2)这个算法就有问题了 虽然用到了dp 但是每次遍历到一个数需要去遍历前面所有的数 找到最大的那个 这样就肯定超时
怎么解决呢?
用一个Hash表存储一下这个末尾数字的最大值好像就可以了~
DP+Map优化:
#include <iostream>
#include<bits/stdc++.h>
#include<unordered_map>
using namespace std;
const int N = 1e5 + 10;
unordered_map<char, int> m;
string arr[N];
int dp[N];
int main()
{
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> arr[i];
dp[i] = 1;//把每个数都初始化为1
}
for (int i = 1; i <= n; i++) {
if (m.count(*arr[i].begin())) { //判断表里是否存有当前数的头
dp[i] += m[*arr[i].begin()];//如果有就加上
}
m[*arr[i].rbegin()]=max(m[*arr[i].rbegin()],dp[i]);//更新尾部的最大值
}
int ans = 0;
for (int i = 1; i <= n; i++) {
ans = max(ans, dp[i]);
}
cout << n - ans;//用总长度减去最长序列 得到要删去的
// 请在此输入您的代码
return 0;
}