題解 P6874 [COCI2013-2014#6] KOCKICE
阿新 • • 發佈:2021-06-27
演算法分析:二分
提供一種基於二分的演算法。
注意一下坑點:
- 最低那一列就是正中間那一列。
- “最低的列左右兩側的磚頭數量必須相同”指的不是與中間列相鄰的兩列,而是所有對應的列。
下面來分析一下如何二分。
這裡二分不直接二分答案。如果直接二分答案,並沒有把原問題變成更簡單的子問題。而二分的核心在於將複雜的原問題轉化為較簡單的子問題。因此,我們二分中間列的高度。
單調性在哪裡?由於相鄰的磚頭高度恰相差 \(1\),中間列高度確定時其他列的磚頭數量也能確定。因此,若中間列高度繼續增加 \(1\) ,可以依此判斷出總操作次數的變化情況。假設當前中間列高度為 \(mid\),那麼第 \(i\)
關於時間複雜度,檢查可行性為\(\mathcal{O}(n)\)
下面給出程式碼:
#include<bits/stdc++.h> #define ll long long #define reg register #define F(i,a,b) for(reg int i=a;i<=b;++i) using namespace std; inline ll read(); const int N=3e5+10; int n,a[N],b[N]; ll ans,sum; inline bool check(ll mid) { int res=0; F(i,1,n){ ll x=mid+abs(n/2+1-i); res+=(a[i]>=x)+(b[i]>=x);//計算操作次數將-1的列數 } return res>=n; } int main() { n=read(); ll l=0,r=0; F(i,1,n)a[i]=read(),r=max(r,(ll)a[i]); F(i,1,n)b[i]=read(),r=max(r,(ll)b[i]); while(l<=r){//二分 ll mid=l+r>>1ll; check(mid)?l=mid+1,ans=mid:r=mid-1; } F(i,1,n){//統計答案 ll x=ans+abs(n/2+1-i); sum+=abs(a[i]-x)+abs(b[i]-x); } printf("%lld",sum); return 0; } inline ll read() { reg ll x=0; reg char c=getchar(); while(!isdigit(c))c=getchar(); while(isdigit(c))x=(x<<1ll)+(x<<3ll)+(c^48),c=getchar(); return x; }
歡迎交流討論,請點個贊哦~