洛谷P1484 種樹&洛谷P3620 [APIO/CTSC 2007]資料備份 題解(堆+貪心)
洛谷P1484 種樹&洛谷P3620 [APIO/CTSC 2007]資料備份 題解(堆+貪心)
標籤:題解
閱讀體驗:https://zybuluo.com/Junlier/note/1329957
題目連結地址:
洛谷P1484 種樹
洛谷P3620 [APIO/CTSC 2007]資料備份(各大oj多倍經驗)
照例吐槽
兩道基本一模一樣的題,只是第二道要差分順便思維稍微向這邊轉化一下。。。
我覺得這兩個題思維很不錯啊!很\(Noip\ T2\)的樣子。。。
話不多說將題解
貪心+堆優化
肯定一開始想到一個\(O(nk)\)的\(dp\)是吧,發現跑不過又優化不了。。。
那和\(dp\)最相近而且時間複雜度低的演算法就是貪心了羅。。。
下面以
不用想了,每次直接選最大值肯定是錯誤的。。。
那怎麼辦?貪心不是有後悔操作嘛!
我們還是直接貪心選最大的,考慮怎麼後悔,是不是與選\(v[i]\)相對的就是選\(v[i-1],v[i+1]\),那麼對於一次後悔,我們可以看做選了\(v[i]\)後,\(Ans+=v[i]\),並且後面我們又選了一個\(v[i-1]+v[i+1]-v[i]\),算一下發現最終就是\(Ans+=v[i+1]+v[i-1]\)是吧,所以我們可以考慮直接把\(v[i]\)的值修改了之後可能重新選一遍
那麼怎麼實現這些操作呢(口糊誰不會。。)
一般這種最大最小加東西刪東西的就可以想到堆啦
考慮用一個大根堆來貪心,每次選出一個最大的元素,然後顯然我們要把這個元素兩邊的元素標記為不能選是吧,那就標記一下唄(因為我們會修改\(v[i]\)的值,所以無所顧忌這兩個東西是否還存在,有點難理解。。。)
然後我們發現當我們修改了元素的值之後,他影響到的左右是不同的,所以我們還需要一個可以支援動態修改左右元素的資料結構,顯然的不就是雙向連結串列嘛。。。
那就做完了
程式碼
壓行永遠是看程式碼的人的噩夢,寫程式碼的人的幸福。。。
洛谷P1484 種樹
#include<bits/stdc++.h> #define il inline #define rg register #define ldb double #define lst long long #define rgt register int #define N 500050 using namespace std; const int Inf=1e9; il int MAX(rgt x,rgt y){return x>y?x:y;} il int MIN(rgt x,rgt y){return x<y?x:y;} il int read() { int s=0,m=0;char ch=getchar(); while(!isdigit(ch)){if(ch=='-')m=1;ch=getchar();} while( isdigit(ch))s=(s<<3)+(s<<1)+(ch^48),ch=getchar(); return m?-s:s; } int n,K;lst Ans; int v[N],le[N],ri[N],vis[N]; struct NODE{int id,w;}; priority_queue<NODE> H; bool operator<(const NODE&a,const NODE&b){return a.w<b.w;} int main() { n=read(),K=read(); for(rgt i=1;i<=n;++i) { v[i]=read(),le[i]=i-1,ri[i]=i+1; H.push((NODE){i,v[i]}); } for(rgt i=1,l,r;i<=K;++i) { while(!H.empty()&&vis[H.top().id])H.pop(); rg NODE tmp=H.top();H.pop();if(tmp.w<0)break; Ans+=tmp.w,l=le[tmp.id],r=ri[tmp.id]; v[tmp.id]=v[l]+v[r]-v[tmp.id]; ri[le[l]]=ri[l],le[ri[l]]=le[l],le[l]=ri[l]=0; ri[le[r]]=ri[r],le[ri[r]]=le[r],le[r]=ri[r]=0; vis[l]=vis[r]=1,H.push((NODE){tmp.id,v[tmp.id]}); }return printf("%lld\n",Ans),0; }
洛谷P3620 [APIO/CTSC 2007]資料備份
需要注意的是:因為變成了最小值,所以邊界可能會減出負數,所以處理下邊界。。。
#include<bits/stdc++.h>
#define il inline
#define rg register
#define ldb double
#define lst long long
#define rgt register int
#define N 100050
using namespace std;
const int Inf=1e9;
il int MAX(rgt x,rgt y){return x>y?x:y;}
il int MIN(rgt x,rgt y){return x<y?x:y;}
il int read()
{
int s=0,m=0;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')m=1;ch=getchar();}
while( isdigit(ch))s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
return m?-s:s;
}
int n,K;lst Ans;
int v[N],le[N],ri[N],vis[N];
struct LINE{int id,x;};
priority_queue<LINE> H;
bool operator<(const LINE&a,const LINE&b){return a.x>b.x;}
int main()
{
n=read(),K=read();
for(rgt i=1;i<=n;++i)v[i]=read();
for(rgt i=1;i<n;++i)
{
v[i]=v[i+1]-v[i];
le[i]=i-1,ri[i]=i+1;
H.push((LINE){i,v[i]});
}v[0]=v[n]=Inf;
for(rgt i=1,l,r;i<=K;++i)
{
while(!H.empty()&&vis[H.top().id])H.pop();
rg LINE tmp=H.top();H.pop();
Ans+=tmp.x,l=le[tmp.id],r=ri[tmp.id];
v[tmp.id]=v[l]+v[r]-v[tmp.id];
ri[le[l]]=ri[l],le[ri[l]]=le[l],le[l]=ri[l]=0;
ri[le[r]]=ri[r],le[ri[r]]=le[r],le[r]=ri[r]=0;
vis[l]=vis[r]=1,H.push((LINE){tmp.id,v[tmp.id]});
}return printf("%lld\n",Ans),0;
}