CF1257E The Contest
洛谷题目链接
题目分析
只要保证\(a_1\)和\(a_3\)满足条件即可,所以我们几乎可以忽略\(a_2\).
为了方便下面讲解,简化一下题面.
将每个元素想象成一棵棵带有编号的树,要通过一系列的操作,
将树种在上方(\(a_1\)处),或种在下方(\(a_3\)处),或因生长发育不合格而被扔掉(\(a_2\)处),然后求最少次数使满足题目要求.
(读者看到此处可根据简化题面再独立思考一下如何解题)
思路解析
最优答案一类的问题,想到DP.
\(F_{i,0/1/2}\)表示所有树里到第i课时扔掉/种在上方/种在下方 的最少操作次数,接下来要分情况讨论(第i棵树的原始状态):
1.在上方
\(F_{i,0}=min(F_{i-1,1},F_{i-1,0})+ 1\) 如果这棵树被扔掉,那前面一棵一定不是种在下面
\(F_{i,1}=F_{i-1,1}\) 这棵树不动,对答案没贡献
\(F_{i,2}=min\) { \(F_{i-1,2},F_{i-1}{1},F_{i-1,0}\)} \(+1\) 如果这棵树被种在下面,则前面树的状态无所谓
2.在下方
\(F_{i,0}=min(F_{i-1,1},F_{i-1,0})+ 1\)
\(F_{i,1}=F_{i-1,1} + 1\)
\(F_{i,2}=min\) { \(F_{i-1,2},F_{i-1}{1},F_{i-1,0}\)}
理解同第一种情况,但要注意是否+1的问题
3.在\(a_2\)垃圾箱里
方程思路与1,2种情况相同,请读者自行推理
4.端点
第一棵树比较特殊,无法从第0棵树转移过来,需要初始化处理一下.
另外说一下时间复杂度,状态转移方程的复杂度是\(O(n)\)但是所有的树需要按照编号从小到大排序,所以时间复杂度为\(O(nlogn)\)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m,t,a[200001],b[200001],c[200001],f[200001][3],d[200001],mx,oo;
inline int pd(int x,int y) {
if(b[x] >= c[y]) return y;
return x;
}
inline bool cmp(int x,int y) {
return x < y;
}
int main() {
scanf("%d%d%d",&n,&m,&t);
for(int i = 1;i <= n; i++) {
int u;
scanf("%d",&u);
d[++mx] = u;
a[u] = 1;
++oo;
}
for(int i = 1;i <= m; i++) {//因为a2可以被忽略,所以不做任何处理
int x;
scanf("%d",&x);
}
for(int j = 1;j <= t; j++) {
int x;
scanf("%d",&x);
d[++mx] = x;
a[x] = 2;
}
n = n + m + t;
sort(d+1,d+mx+1,cmp);
if(a[1] == 1) f[1][0] = f[1][2] = 1;
if(a[1] == 0) f[1][1] = f[1][2] = 1;
if(a[1] == 2) f[1][1] = f[1][0] = 1;
for(int i = 2;i <= n; i++) {
if(a[i] == 0) {
f[i][0] = min(f[i-1][1],f[i-1][0]);
f[i][1] = f[i-1][1] + 1;
f[i][2] = min(f[i-1][2],min(f[i-1][0],f[i-1][1])) + 1;
}
if(a[i] == 2) {
f[i][0] = min(f[i-1][1],f[i-1][0]) + 1;
f[i][1] = f[i-1][1] + 1;
f[i][2] = min(f[i-1][2],min(f[i-1][1],f[i-1][0]));
}
if(a[i] == 1) {
f[i][0] = min(f[i-1][1],f[i-1][0]) + 1;
f[i][1] = f[i-1][1];
f[i][2] = min(f[i-1][2],min(f[i-1][1],f[i-1][0])) + 1;
}
}
printf("%d",min(f[n][1],min(f[n][0],f[n][2])));
return 0;
}