CodeForces - 1313C2 Skyscrapers (hard version)(单调栈+dp/分治)
題目鏈接:點擊查看
題目大意:給出 n 塊連續(xù)的空地可以建造摩天大樓,政府有規(guī)定,每塊地最高只能建 a[ i ] 的高度,同時每棟大樓需要滿足一個規(guī)則,即每棟大樓的兩側(cè)不允許同時存在比自己高的大樓,輸出一種方案,使得總高度之和最大
題目分析:C1題目的數(shù)據(jù)范圍是 1e3 ,直接兩層 for 暴力枚舉每個位置作為最高點就可以了,沒有難度的暴力就不多說了,直接說一下C2題目的 5e5 該如何處理吧
看到 5e5 這個數(shù)字,再看到題目給出了 3 秒的時限,第一反應肯定是摒棄 n * n 的算法,從而需要思考能否在O(n),O(nlogn)或O(nsqrt(n))中做出選擇,賽后看牛客的群中有大牛用了nsqrt(n)分塊的做法做出來了所以提一嘴。。其實比賽的時候隊友已經(jīng)想出該如何用 dp O(n) 維護答案了,設dp1[ i ]為正著到第 i 個位置為最高點的最大高度和,dp2[ i ]為反著到第 i 個位置為最高點的最大高度和,預處理完兩個dp數(shù)組后,就可以O(n)枚舉每個位置作為最高點,并且O(1)獲得此時的貢獻了,此時確定好最高點的位置剩下的輸出就不多說了,現(xiàn)在的問題轉(zhuǎn)換為了如何預處理出兩個dp數(shù)組,其實不難,以dp1為例,當處理到dp[ i ]時,我們只需要找到一個位置 pos ,這個位置是位置 i 左邊第一個比 a[ i ] 小的位置,此時 dp[ i ] = dp[ pos ] + ( i - pos ) * a[ i ] ,怎么解釋呢,因為 pos 是位置 i 左側(cè)第一個比 a[ i ] 小的位置,所以區(qū)間 ( pos , i ] 內(nèi)的所有高度一定是大于等于 a[ i ] 的,因為我們的dp數(shù)組是求以點 i 為最高點時的最大高度和,所以點 i 的高度肯定是最大高度了,顯然區(qū)間 ( pos , i ] 內(nèi)的所有點都賦值為 a[ i ] 是最優(yōu)的,而此時再累加上dp[ pos ]的答案便是當前點 i 的答案了
剩下的問題就變的很簡單了,如何求出每個點左邊以及右邊第一個小于自己的位置呢,單調(diào)棧的基礎應用,直接套上模板,O(n)維護出位置就可以了,總的時間復雜度為O(n),帶著一點常數(shù)無傷大雅
代碼的話別看著很長,其實復用的部分很多,實際的編程復雜度充其量也只有一半多一點吧,剩下的都是CV大法了
2020.2.25更新:
后來看別人代碼發(fā)現(xiàn)分治也可以做,而且不管是想起來還是寫起來難度都比較低,于是回來補一下,分治的solve( l , r )表示在區(qū)間[ l , r?]中尋找答案,顯然當 l > r 時返回 0 ,當 l == r 時返回 a[i],考慮一般情況,因為題意可以轉(zhuǎn)換為每個點只能有一側(cè)大于當前點的高度,另一側(cè)的所有大樓高度需要小于等于當前的高度,這樣每次我們可以尋找[ l , r ]中的最小值所在的位置,分兩種情況,一種是令其左側(cè)的高度都等于最小值,另一種則是令其右側(cè)的高度都等于最小值,兩個答案選擇較優(yōu)的返回即可,時間復雜度最壞會退化到O( n ),而尋找區(qū)間最小值的位置可以交給線段樹處理,logn時間內(nèi)查找最小值,總時間復雜度為nlogn
代碼:
單調(diào)棧+dp:
#include<iostream> #include<cstdio> #include<string> #include<ctime> #include<cmath> #include<cstring> #include<algorithm> #include<stack> #include<climits> #include<queue> #include<map> #include<set> #include<sstream> using namespace std;typedef long long LL;typedef unsigned long long ull;const int inf=0x3f3f3f3f;const int N=5e5+100;LL a[N],dp1[N],dp2[N];int l[N],r[N];int main() { #ifndef ONLINE_JUDGE // freopen("input.txt","r",stdin); // freopen("output.txt","w",stdout); #endif // ios::sync_with_stdio(false);int n;scanf("%d",&n);for(int i=1;i<=n;i++)scanf("%lld",a+i);a[0]=a[n+1]=-inf;stack<int>st;st.push(0);for(int i=1;i<=n;i++){while(st.size()&&a[st.top()]>=a[i])st.pop();l[i]=st.top();st.push(i);}while(st.size())st.pop();st.push(n+1);for(int i=n;i>=1;i--){while(st.size()&&a[st.top()]>=a[i])st.pop();r[i]=st.top();st.push(i);}for(int i=1;i<=n;i++)dp1[i]=dp1[l[i]]+(i-l[i])*a[i];for(int i=n;i>=1;i--)dp2[i]=dp2[r[i]]+(r[i]-i)*a[i];LL mmax=-inf,mark;for(int i=1;i<=n;i++)if(mmax<dp1[i]+dp2[i]-a[i]){mmax=dp1[i]+dp2[i]-a[i];mark=i;}deque<LL>ans;ans.push_back(a[mark]);LL pre=a[mark];for(int i=mark-1;i>=1;i--){pre=min(pre,a[i]);ans.push_front(pre);}pre=a[mark];for(int i=mark+1;i<=n;i++){pre=min(pre,a[i]);ans.push_back(pre);}for(int i=0;i<ans.size();i++)printf("%d ",ans[i]);return 0; }分治:
#include<iostream> #include<cstdio> #include<string> #include<ctime> #include<cmath> #include<cstring> #include<algorithm> #include<stack> #include<climits> #include<queue> #include<map> #include<set> #include<sstream> using namespace std;typedef long long LL;typedef unsigned long long ull;const int inf=0x3f3f3f3f;const int N=5e5+100;int ans[N];struct Min {int val,pos;static Min INF(){Min ans;ans.pos=ans.val=inf;return ans;}bool operator<(const Min& a)const{if(a.val!=val)return val<a.val;return pos<a.pos;} };struct Node {int l,r;Min mmin; }tree[N<<2];void pushup(int k) {if(tree[k<<1].mmin<tree[k<<1|1].mmin)tree[k].mmin=tree[k<<1].mmin;elsetree[k].mmin=tree[k<<1|1].mmin; }void build(int k,int l,int r) {tree[k].l=l;tree[k].r=r;if(l==r){tree[k].mmin.pos=l;scanf("%d",&tree[k].mmin.val);return;}int mid=l+r>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);pushup(k); }Min query(int k,int l,int r) {if(tree[k].l>r||tree[k].r<l)return Min::INF();if(tree[k].l>=l&&tree[k].r<=r)return tree[k].mmin;return min(query(k<<1,l,r),query(k<<1|1,l,r)); }LL solve(int l,int r) {if(l>r)return 0LL;if(l==r){ans[l]=query(1,l,r).val;return ans[l];}Min mmin=query(1,l,r);int pos=mmin.pos;LL val=mmin.val;LL sum1=solve(l,pos-1)+(r-pos+1)*val;LL sum2=solve(pos+1,r)+(pos-l+1)*val;if(sum1>sum2)for(int i=pos;i<=r;i++)ans[i]=val;elsefor(int i=l;i<=pos;i++)ans[i]=val;return max(sum1,sum2); }int main() { #ifndef ONLINE_JUDGE // freopen("input.txt","r",stdin); // freopen("output.txt","w",stdout); #endif // ios::sync_with_stdio(false);int n;scanf("%d",&n);build(1,1,n);solve(1,n);for(int i=1;i<=n;i++)printf("%d ",ans[i]);return 0; }?
總結(jié)
以上是生活随笔為你收集整理的CodeForces - 1313C2 Skyscrapers (hard version)(单调栈+dp/分治)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: XJOJ - 路径数(最短路+最短路路径
- 下一篇: CodeForces - 1313B D