BZOJ 1150: [CTSC2007]數據備份Backup 優先隊列+貪心+鏈表
阿新 • • 發佈:2019-02-10
for span 數據備份 str 最小 同時 struct 之間 pre
題解:我們先得到兩個樓之間的距離,D[i]表示第i棟樓和第i+1棟樓之間的距離,我們要選出最小的k個數,然後就有兩種情況
1.選擇了D[i],那麽D[i-1]和D[i+1]都不能選擇了
2.選擇了D[i+1]和D[i-1],然後無法選擇D[i].
既要麽選D[i]不選D[i-1]和D[i+1],要麽選D[i-1]和D[i+1]不選D[i];(怎麽證明的也搞不太清楚)我們用優先隊列來得到每次最小的距離,
我們發現在選取一個數之後,只對左右兩邊的數有影響,選了D[i]後,我們把D[i-1]+D[i+1]-D[i]加入隊列,同時D[i-1]和D[i+1]不能再選了,我們就把D[i]旁邊的邊刪除,然後再打上標記
和雙鏈表中的刪除一個節點類似,這裏用數組代替了鏈表。
還有由於我的優先隊列裏放的是整個結構體,所以每次取出堆頂後,L和R一定要使用數組中的,而不能直接使用當前取出的節點的L和R(這個地方wa了好幾次),因為有些樓的左右節點變了,而優先隊列中的還沒變。
#include<bits/stdc++.h> using namespace std; const int inf = 0x3f3f3f3f; const int maxn = 2e5 + 5; //數組代替鏈表 每個節點代表一棟樓的信息,以及左邊,右邊的樓的編號 struct node{ int pos, dis, Lpos, Rpos; node(int pos = 0, int dis = 0, int Lpos = 0, int Rpos = 0){ pos = pos, dis = dis, Lpos = Lpos, Rpos = Rpos; } bool operator<(const node &other)const{ return dis > other.dis; } }a[maxn]; bool vis[maxn]; int b[maxn],n,k; priority_queue<node> que; long long ans; void solve() {while (k--) { while (1) { node cur = que.top(); que.pop(); if (vis[cur.pos]) continue; int pos = cur.pos, Lpos = a[pos].Lpos, Rpos = a[pos].Rpos; /*優先隊列中放整個結構體的一定要註意這個地方,這裏Lpos和Rpos一定要用 數組中的L,R,而不能直接使用cur.Lpos,cur.Rpos 我在這裏卡了好長時間*/ vis[Lpos] = vis[Rpos] = true; //這個樓已使用,那麽它旁邊的樓就不能再使用,打上標記,pos這個位置還會繼續使用,因此pos不打標記 ans += 1LL*cur.dis; a[pos].dis = a[Lpos].dis + a[Rpos].dis - a[pos].dis;// pos位置繼續使用,但改變距離 a[pos].Lpos = a[Lpos].Lpos; a[pos].Rpos = a[Rpos].Rpos; a[a[Rpos].Rpos].Lpos = pos; a[a[Lpos].Lpos].Rpos = pos; //改變左右相鄰的樓 que.push(a[pos]); break; } } } int main() { cin >> n >> k; for (int i = 1; i <= n; i++) { cin >> b[i]; } for (int i = 1; i < n; i++) { a[i].dis = b[i + 1] - b[i]; a[i].pos = i; a[i].Lpos = i - 1; a[i].Rpos = i + 1; que.push(a[i]); } a[n - 1].Rpos = 0; a[0].dis = a[n].dis = inf; solve(); cout << ans; return 0; }
再來一個不放結構體的
struct cmp{ bool operator()(int a, int b) { return D[a] > D[b]; } }; priority_queue<int, vector<int>, cmp>;
BZOJ 1150: [CTSC2007]數據備份Backup 優先隊列+貪心+鏈表