【BZOJ1150】数据备份(堆/优先队列)

传送门

  • 题目:在这里插入图片描述
  • 思路:
    先获取每两个相邻办公楼之间的距离。 D i D_i Di(1≤i≤n-1)
    • 当k=1时,选择最小的 D i D_i Di

    • 当k=2时

      • 方案一:选择最小的 D i D_i Di和除了 D i − 1 , D i , D i + 1 D_{i-1},D_i,D_{i+1} Di1,Di,Di+1之外的其他数中的最小值
      • 方案二:选择最小的 D i D_i Di旁边的 D i − 1 , D i + 1 D_{i-1},D_{i+1} Di1,Di+1

      由于不知道每次是选择第一种方案还是第二种,所以每次先将最小的 D i D_i Di累加入答案,再将 D i − 1 , D i , D i + 1 D_{i-1}, D_i, D_{i+1} Di1,Di,Di+1归为 D i − 1 + D i + 1 − D i D_{i-1}+D_{i+1}-D_i Di1+Di+1Di,如果下次选择 D i − 1 + D i + 1 − D i D_{i-1}+D_{i+1}-D_i Di1+Di+1Di的话,那么就是属于第二种情况,否则就是第一种。

实现:
(1)链表存 D 1 , . . . , D n − 1 D_1,...,D_{n-1} D1,...,Dn1并记录每个 D i D_i Di在小根堆的编号,同样小根堆里也要记录每个编号上的 D i D_i Di在链表中的位置。因为每次要处理的是当前的最小值以及最小值的前后两个数据。注意,这种方法需要特别处理最小值 D i D_i Di没有前驱或者后继的情况,按照上述思路这种情况只能是第一种方案。
(2)优先队列实现。因为每次的 D i − 1 , D i , D i + 1 D_{i-1}, D_i, D_{i+1} Di1,Di,Di+1要归为 D i − 1 + D i + 1 − D i D_{i-1}+D_{i+1}-D_i Di1+Di+1Di,相当于 D i − 1 , D i + 1 D_{i-1}, D{i+1} Di1,Di+1被删除, D i D_i Di D i − 1 + D i + 1 − D i D_{i-1}+D_{i+1}-D_i Di1+Di+1Di代替。可以用数组标记下哪些下标对应的 D D D值被删除了,今后便不再处理中这些位置上的数据。为了避免用链表实现是的特殊情况,可以将 D n − 1 D_{n-1} Dn1的后继定为 D 0 D_0 D0,且 D 0 D_0 D0无穷大。

  • 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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
BZOJ 2908 题目是一个数据下载任务。这个任务要求下载指定的数据文件,并统计文件中小于等于给定整数的数字个数。 为了完成这个任务,首先需要选择一个合适的网址来下载文件。我们可以使用一个网络爬虫库,如Python中的Requests库,来帮助我们完成文件下载的操作。 首先,我们需要使用Requests库中的get()方法来访问目标网址,并将目标文件下载到我们的本地计算机中。可以使用以下代码实现文件下载: ```python import requests url = '目标文件的网址' response = requests.get(url) with open('本地保存文件的路径', 'wb') as file: file.write(response.content) ``` 下载完成后,我们可以使用Python内置的open()函数打开已下载的文件,并按行读取文件内容。可以使用以下代码实现文件内容读取: ```python count = 0 with open('本地保存文件的路径', 'r') as file: for line in file: # 在这里实现对每一行数据的判断 # 如果小于等于给定整数,count 加 1 # 否则,不进行任何操作 ``` 在每一行的处理过程中,我们可以使用split()方法将一行数据分割成多个字符串,并使用int()函数将其转换为整数。然后,我们可以将该整数与给定整数进行比较,以判断是否小于等于给定整数。 最后,我们可以将统计结果打印出来,以满足题目的要求。 综上所述,以上是关于解决 BZOJ 2908 数据下载任务的简要步骤和代码实现。 希望对您有所帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值