数据备份
- 题目链接
- 数据结构/优先队列/链表
- STL/list
- 贪心
Description
有 N 座办公楼位于同一条街上,已知每座办公楼距离大街起点的距离。现要用 K 条网络电缆将办公楼连接,且任意一个办公楼都属于唯一配对组(即连接的 2K 个办公楼一定是相异的)。求 K 条网络电缆最小的长度总和。
Solution
容易发现,最优解中每两个配对的办公楼一定是相邻的。于是先求出两个相邻办公楼之间的距离,记为 D1,D2,D3,...,DN−1。于是问题可以转化为:从数列 D 中选取不超过 K 个不相邻的数,使它们的和最小。
考虑贪心:
- 如果 K=1,答案显然是数列 D 中的最小值
- 如果 K=2,可以证明答案是以下两种情况之一:
- 选取 Di,以及除了 Di−1,Di,Di+1 之外其他数中的最小值
- 选取最小值 Di左右两侧的两个数,即 Di−1 和 Di+1
- 即最优解中,最小值左右两侧的数要么选,要么都不选
- 为防止选取局部最优解,可以先选取数列 D 中的最小值 Di,然后把 Di−1,Di,Di+1 删除,把 Di−1+Di+1−Di 插入到数列 D 中之前执行删除的位置。这样如果之后选了 Di−1+Di+1−Di,相当于没有选 Di,换上了 Di−1 和 Di+1。
于是可建立一个初始有 N−1 个节点的链表,链表节点记录数值 Di,节点编号 Idi,以及链表节点指针或对应的迭代器。再建立一个优先队列(小根堆),将链表节点存入。K 次操作中,取出堆顶的链表节点 ListNode,在链表中删除 ListNode−>prev 和 ListNode−>next,并创建新节点(数值为 ListNode−>prev−>data+ListNode−>next−>data−ListNode−>data)插入链表和优先队列。
注意:为防止越界或简化边界情况判断,可以在链表起始和末尾插入极大值。
Code
指针版双向链表
指针版双向链表1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
| #include <bits/stdc++.h>
using namespace std; using LL = long long;
constexpr int MAXN = 1e5 + 5; constexpr LL INF = 1e18;
LL n, k, dis[MAXN], ans, cnt;
struct ListNode { ListNode() = default; bool operator<(const ListNode &other) const { return this->data == other.data ? this->id > other.id : this->data > other.data; } ListNode *prev = NULL, *next = NULL, *cur = NULL; LL data = 0, id = -1; }; map<int, bool> deleted;
struct List { List() { this->head = new ListNode(), this->tail = new ListNode(); this->head->next = this->tail; this->tail->prev = this->head; }
ListNode *insert(LL data) { ListNode *cur = new ListNode(); cur->data = data, cur->id = cnt++, cur->cur = cur; tail->prev->next = cur, cur->prev = tail->prev; cur->next = tail, tail->prev = cur; return cur; }
ListNode *erase(ListNode *cur) { LL data = cur->prev->data + cur->next->data - cur->data; ListNode *newListNode = new ListNode(); newListNode->cur = newListNode; newListNode->data = data, newListNode->id = cnt++;
ListNode *prev = cur->prev, *next = cur->next; prev->prev->next = newListNode, newListNode->prev = prev->prev; next->next->prev = newListNode, newListNode->next = next->next;
deleted[prev->id] = deleted[next->id] = deleted[cur->id] = true; return newListNode; }
~List() {} ListNode *head = NULL, *tail = NULL; } lst;
priority_queue<ListNode> q; inline void calcLen(int tot = 0) { while (!q.empty() && tot < k) { ListNode* cur = q.top().cur; q.pop(); if (deleted[cur->id]) continue; ans += cur->data, tot++; ListNode *newListNode = lst.erase(cur); q.push(*newListNode); } }
int main() { scanf("%lld %lld", &n, &k); for (int i = 0; i < n; ++i) scanf("%lld", dis + i); lst.insert(INF); for (int i = 0; i < n - 1; ++i) q.push(*lst.insert(dis[i + 1] - dis[i])); lst.insert(INF); calcLen(); printf("%lld\n", ans); return 0; }
|
STL list
STL 的 list 为 环形双向链表, 本题中用到以下功能。
- 通过 iterator−− 或 iterator++ 获得 prev 或 next 的迭代器。
- insert() 成员函数在指定位置处插入值,并返回插入的元素所对应的的迭代器
- erase() 成员函数对指定位置进行删除,返回删除的元素后一个位置的迭代器
STL list1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| #include <bits/stdc++.h>
using namespace std; using LL = long long;
constexpr int MAXN = 1e5 + 5; constexpr LL INF = 1e15;
LL n, k, dis[MAXN], ans, cnt;
struct ListNode { ListNode() = default; ListNode(LL _data, LL _id) : data(_data), id(_id) {} bool operator<(const ListNode &other) const { return this->data == other.data ? this->id > other.id : this->data > other.data; } LL data = INF, id = -1; list<ListNode>::iterator itr; }; list<ListNode> lst; priority_queue<ListNode> q; map<LL, bool> deleted;
inline void calcLen(int tot = 0) { while (!q.empty() && tot < k) { auto cur = q.top(); q.pop(); if (deleted[cur.id]) continue; ans += cur.data, tot++; auto itr = cur.itr; --itr; LL data = itr->data; deleted[itr->id] = true, itr = lst.erase(itr); data -= itr->data, deleted[itr->id] = true, itr = lst.erase(itr); data += itr->data, deleted[itr->id] = true, itr = lst.erase(itr); auto newNode = ListNode(data, cnt++); newNode.itr = lst.insert(itr, newNode); q.push(newNode); } }
int main() { std::ios::sync_with_stdio(false), cin.tie(0), cout.tie(0); cin >> n >> k; for (int i = 0; i < n; ++i) cin >> dis[i]; lst.emplace_back(ListNode(INF, cnt++)); for (int i = 0; i < n - 1; ++i) { auto newNode = ListNode(dis[i + 1] - dis[i], cnt++); newNode.itr = lst.insert(lst.end(), newNode); q.push(newNode); } lst.emplace_back(ListNode(INF, cnt++)); calcLen(); cout << ans << endl; return 0; }
|
类似题目
种树
国家集训队 种树
BACKUP Backup Files