[CTSC2007][APIO2007]數據備份Backup
阿新 • • 發佈:2017-07-11
ron 連接 結果 brush 數據備份 () ons bzoj1150 問題:
題目:BZOJ1150、codevs1615、洛谷P3620
題目大意:有n個點,k條鏈,每個點離原點有一定的距離。要你用k條鏈連接2k個點,使得k條鏈的長度最短。
解題思路:畢竟是CTSC級別的題目,很難找出正確算法。在網上翻閱了很多資料後,終於理解了此題的正確算法。orz
正確算法為:貪心+鏈表+堆。
首先每次肯定是鏈相鄰的2個點,所以我們先把相鄰2個點的差值求出來,得到有n-1個數的數列。
然後問題就變成“在這個數列中尋找k個互不相鄰的點,使得它們的和最小”。
我們把所有的數扔進一個堆裏,每次貪心找出最小的數,在答案裏把它加上。
這時就會出現一個問題:假設我們貪心出了第i個數,那麽有可能選第i-1個數和第i+1個數最終的結果優於選第i個數。
所以我們每次貪心時,把找到的那個數的前一個數和後一個數刪掉,把“前一個數+後一個數-原數”的值扔進堆裏,如果這個數被選,相當於選了前一個數和後一個數而不選原數。
而鏈表記錄的是每個數的前一個和後一個。詳見代碼第32~35行。
在刪數時,並不需要在堆裏找到這個數,只需把它的值改為inf就行了。詳見代碼第36行。
C++ Code:
#include<cstdio> #include<ext/pb_ds/priority_queue.hpp> const int inf=int(1000000000*1.3); using namespace std; struct shuju{ int dis,num; shuju(int d,int n):dis(d),num(n){} bool operator<(const shuju& rhs)const{return dis>rhs.dis;} }; __gnu_pbds::priority_queue<shuju,less<shuju>,__gnu_pbds::pairing_heap_tag>h; int n,k,dis[100005],pre[100005],nxt[100005]; int main(){ scanf("%d%d",&n,&k); int x,y; scanf("%d",&x); for(int i=2;i<=n;i++){ scanf("%d",&y); dis[i]=y-x;x=y; h.push(shuju(dis[i],i)); pre[i]=i-1;nxt[i]=i+1; } int ans=0; pre[2]=nxt[n]=0; while(k--){ while(h.top().dis!=dis[h.top().num])h.pop(); int k=h.top().num; int l=pre[k],r=nxt[k]; h.pop(); ans+=dis[k]; if(l&&r)dis[k]=dis[l]+dis[r]-dis[k];else dis[k]=inf; pre[nxt[r]]=k; nxt[pre[l]]=k; nxt[k]=nxt[r]; pre[k]=pre[l]; dis[l]=dis[r]=inf; h.push(shuju(dis[k],k)); } printf("%d\n",ans); return 0; }
[CTSC2007][APIO2007]數據備份Backup