洛谷題目鏈接
題目分析
只要保證\(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;
}