2 条题解
-
019210237 (笨笨童年) LV 8 MOD @ 2023-07-08 18:51:24
// // main.cpp // 2 // // Created by 汪汪队 on 2023/7/8. // #include<iostream> #include<algorithm> #include<set> #include<unordered_map> #include<queue> #include<vector> #define int long long using namespace std; const int N=200010; int dp[N]; int min(int a,int b,int c){ return min(min(a,b),c); } void test(){ int n; cin>>n; vector<int>p(n+2); for (int i=0; i<n; i++) { cin>>p[i]; dp[i]=0x3f3f3f3f; } unordered_map<int,int>_map; dp[0]=1; _map[p[0]]=0; for (int i=1; i<n; i++) { int k=p[i]; if(!_map.count(k)){ _map[k]=i; dp[i]=min(dp[i],dp[i-1]+1); } else{ dp[i]=min(dp[_map[k]-1],dp[i-1]+1,dp[i]); // _map[k].push_back(i); if(dp[i-1]<dp[_map[k]-1]) _map[k]=i; // for (int j=0; j<_map[k].size(); j++) { // if(_map[k][j]==0){ // dp[i]=0; // break; // } // dp[i]=min(dp[_map[k][j]-1],dp[i-1]+1,dp[i]); // } } } // for (int i=0; i<n; i++) { // cout<<dp[i]<<" "; // } // cout<<endl; cout<<n-dp[n-1]<<endl; return; } signed main() { ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); int n; cin>>n; while (n--) { test(); } return 0; }
-
02023-07-05 13:08:02@
F. Tenzing and Balls
Solution
Analysis
我们用数值来代表每个小球的颜色,题意即为每次操作可以取出序列中两个数值相同的之间的所有数(包括这两个数),并且将序列变成删除子序列后的新序列。
分析题目,显然:
对于 \(a < b < c < d\) 显然我们不可能删除同时范围 \((a, c)\) 和 \((b, d)\) ,因为当我们删除序列 \([a, c]\) 后序列将会变成 \([c, d]\) 。
如果我们同时删除范围 \((a, d)\) 和 \((b, c)\) ,最终得到的新序列应该与仅删除范围 \((a, d)\) 结果相同,换句话说:当序列 \([b, c] \subset [a, d]\) 时,结果即为删除序列 \([a, d]\) ,我们只需要进行一次操作即可。
那么,经过上述分析,我们可以假设:
在我们所寻求的最优解中,所有删除的子序列都是不相交的。
因此,问题等价为:我们需要找到一些不相交的子序列集合 \(A_n = \{ a[l_i, r_i], i\in [1, n] \}\) ,
使得对于 \(\forall \ a[l_i, r_i] \bigcap a[l_j, r_j] = \varnothing (i,j \in [1,n])\) 并且 \(a[l_i] = a[r_i]\) ,同时我们也需要最大化 \(\sum_{i=1}^n (r_i - l_i + 1)\) 的值。
Conclusion
考虑使用动态规划解决等价问题,令 \(dp_i\) 代表在子序列前端 \(a[1 \cdots n]\) 中==不删除的点==的最小数量,那么最终我们需要的最大值就是总数量减去前 \(n\) 个数中不删除的点的最小数量。
分析状态转移,用 \(j\) 来代表我们状态转移中寻求的索引,\(dp_j\) 即代表为每个数值 \(x\) 存储的满足 \(a[j+1]=x\) 的最小值,分析能否寻找到这样的索引 \(j\) ,我们可以得到:
- 如果我们无法寻找到一个索引 \(j\) 使得 \(a[j+1] = a[i]\) ,即我们无法删除子序列 \(a[j+1 \cdots i]\) ,此时状态转移为 \(dp_i = dp_{i-1} + 1\) 。
- 若果我们可以寻找到一个索引 \(j\) 使得 \(a[j+1] = a[i]\) ,即我们能够删除子序列 \(a[j+1 \cdots i]\) ,此时状态转移为在 \(a[j+1]=a[i] \) 且 \(j + 1 < i\) 的情况下求解 \(dp_i = min\{dp_j\}\) 。
综合所有情况,我们得出最终的状态转移方程:
\[
dp_i = min(dp_{i - 1} + 1, \ min\{dp_j | j+1<i, a[j+1]=a[i]\})
\]
我们预先为每个数值 \(x\) 存储满足 \(a[j + 1] = x\) 的最小 \(dp_j\) ,这样计算每个 \(dp\) 的时间复杂度为 \(\Theta(n)\) 。Code
#include <bits/stdc++.h> #define mod 998244353 #define N 200005 #define INI 1000000000 using namespace std; using ll = long long int; int n, a[N], dp[N], mn[N]; void solve() { cin >> n; dp[0] = 0; dp[1] = 0; for(int i = 1; i <= n; i++) { mn[i] = INI; } for(int i = 1; i <= n; i++) { cin >> a[i]; dp[i] = min(dp[i - 1] + 1, mn[a[i]]); mn[a[i]] = min(mn[a[i]], dp[i - 1]); } cout << n - dp[n] << endl; } int main() { ios::sync_with_stdio(false); cin.tie(0), cout.tie(0); int T = 1; cin >> T; while (T--) solve(); return 0; }
- 1
信息
- ID
- 1430
- 难度
- (无)
- 分类
- (无)
- 标签
- (无)
- 递交数
- 0
- 已通过
- 0
- 通过率
- ?
- 上传者