- 題目:
- 思路:
先獲取每兩個相鄰辦公樓之間的距離。(1≤i≤n-1)-
當k=1時,選擇最小的
-
當k=2時
- 方案一:選擇最小的和除了之外的其他數中的最小值
- 方案二:選擇最小的旁邊的
由於不知道每次是選擇第一種方案還是第二種,所以每次先將最小的累加入答案,再將歸爲,如果下次選擇的話,那麼就是屬於第二種情況,否則就是第一種。
-
實現:
(1)鏈表存並記錄每個在小根堆的編號,同樣小根堆裏也要記錄每個編號上的在鏈表中的位置。因爲每次要處理的是當前的最小值以及最小值的前後兩個數據。注意,這種方法需要特別處理最小值沒有前驅或者後繼的情況,按照上述思路這種情況只能是第一種方案。
(2)優先隊列實現。因爲每次的要歸爲,相當於被刪除,用代替。可以用數組標記下哪些下標對應的值被刪除了,今後便不再處理中這些位置上的數據。爲了避免用鏈表實現是的特殊情況,可以將的後繼定爲,且無窮大。
- ac代碼:
(1)鏈表和小根堆實現:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+10;
struct LinkNode{
ll val;
int pre, nxt, hid;//在堆中對應的id
}link[maxn];
struct HeapNode{
ll val;
int lid;//在鏈表中對應的id
}heap[maxn];
int n, k, tot;
ll ans;
void up(int p)
{
while(p>1)
{
if(heap[p].val<heap[p/2].val)
{
swap(heap[p], heap[p/2]);
swap(link[heap[p].lid].hid, link[heap[p/2].lid].hid);
p/=2;
}
else break;
}
}
void down(int p)
{
int pp = p*2;
while(pp<=tot)
{
if(pp<tot && heap[pp].val>heap[pp+1].val) pp++;
if(heap[p].val>heap[pp].val)
{
swap(heap[p], heap[pp]);
swap(link[heap[p].lid].hid, link[heap[pp].lid].hid);
p = pp; pp = p*2;
}
else break;
}
}
void del_link(int p)
{
link[link[p].pre].nxt = link[p].nxt;
link[link[p].nxt].pre = link[p].pre;
}
void del_heap(int p)
{
swap(link[heap[p].lid].hid, link[heap[tot].lid].hid);
heap[p] = heap[tot--];
up(p); down(p);
}
int main()
{
//freopen("/Users/zhangkanqi/Desktop/11.txt","r",stdin);
int pre = 0, now = 0, nxt = 0;
ll v;
scanf("%d %d %d", &n, &k, &pre); tot = n-1;
for(int i = 1; i < n; i++)
{
scanf("%d", &now); v = now-pre;
link[i] = {v, i-1, i+1, i};
heap[i] = {v, i}; up(i);
pre = now;
}
for(int i = 1; i <= k; i++)
{
ans += heap[1].val;
now = heap[1].lid; pre = link[now].pre; nxt = link[now].nxt;//確定堆頂在鏈表中的的pre,now,nxt
if(pre==0 || nxt==n)
{
if(pre==0) del_link(nxt), del_heap(link[nxt].hid);
if(nxt==n) del_link(pre), del_heap(link[pre].hid);
del_link(now), del_heap(1);
}
else
{
v = link[pre].val+link[nxt].val-link[now].val;
heap[1].val = link[now].val = v; down(1);//更新堆頂並down
del_link(pre), del_heap(link[pre].hid);
del_link(nxt), del_heap(link[nxt].hid);
}
}
printf("%lld\n", ans);
return 0;
}
(2)優先隊列和數組標記實現:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+10;
int n, k;
ll ans;
bool del[maxn];
int pre[maxn], nxt[maxn];
ll v[maxn];
struct node{
int id;
ll val;
friend bool operator <(node a, node b)
{
return a.val>b.val;
}
};
priority_queue<node> q;
int main()
{
//freopen("/Users/zhangkanqi/Desktop/11.txt","r",stdin);
ll last, now;
scanf("%d %d %lld", &n, &k, &last);
for(int i = 1; i < n ; i++)
{
scanf("%lld", &now);
v[i] = now-last;
q.push({i, v[i]});
pre[i] = i-1; nxt[i] = i+1==n?0:i+1;//構成循環,不需要再考慮特殊的前驅和後繼
last = now;
}
v[0] = INT_MAX;
while(k)
{
node x= q.top(); q.pop();
int id = x.id;
if(del[id]) continue;
ans += v[id];
del[pre[id]] = true; del[nxt[id]] = true;
v[id] = v[pre[id]]+v[nxt[id]]-v[id]; q.push({id, v[id]});
pre[id] = pre[pre[id]], nxt[id] = nxt[nxt[id]];
nxt[pre[id]] = id, pre[nxt[id]] = id;//pre[x],nxt[x]已經由上一行改變
k--;
}
printf("%lld\n", ans);
return 0;
}
此外還學到了一種重載運算符的方法,第一次見:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+10;
int n, k;
ll ans;
bool del[maxn];
int pre[maxn], nxt[maxn];
ll v[maxn];
struct cmp{
bool operator()(int a, int b)
{
return v[a]>v[b];
}
};
priority_queue<int, vector<int>, cmp> q;
int main()
{
//freopen("/Users/zhangkanqi/Desktop/11.txt","r",stdin);
ll last, now;
scanf("%d %d %lld", &n, &k, &last);
for(int i = 1; i < n ; i++)
{
scanf("%lld", &now);
v[i] = now-last;
q.push(i);
pre[i] = i-1; nxt[i] = i+1==n?0:i+1;//構成循環,不需要再考慮特殊的前驅和後繼
last = now;
}
v[0] = INT_MAX;
while(k)
{
int x = q.top(); q.pop();
if(del[x]) continue;
ans += v[x];
del[pre[x]] = true; del[nxt[x]] = true;
v[x] = v[pre[x]]+v[nxt[x]]-v[x]; q.push(x);
pre[x] = pre[pre[x]], nxt[x] = nxt[nxt[x]];
nxt[pre[x]] = x, pre[nxt[x]] = x;//pre[x],nxt[x]已經由上一行改變
k--;
}
printf("%lld\n", ans);
return 0;
}