• 【luogu P3620】数据备份(反悔贪心 / 撤回贪心)


    数据备份

    题目链接:luogu P3620

    题目大意

    给你一个数轴上面的一些点,你要选若干个点对,使得每个点至多在一个点对中,而且要你最小化每个点对之间距离的和。

    思路

    首先考虑普通的贪心。
    就是每次选费用最小的那个,然后把它两边可以选的删掉。

    但是你可能选旁边两个而不选它更优,所以考虑一个撤回操作:
    你选了之后删掉两个旁边的和自己之后,加入旁边两个减去自己的值,那选了这个就抵消了这次选自己的,然后选了旁边的。(总体来讲也是多选了一个)

    然后因为你旁边可能早就被删了,那你要找到的是旁边第一个没有被删的,所以我们可以用双向链表来维护。

    代码

    #include<queue>
    #include<cstdio>
    #define ll long long
    
    using namespace std;
    
    struct pl {
    	int val, l, r;
    }a[100001];
    int n, k, x, lst;
    bool cnot[100001];
    ll ans;
    
    struct node {
    	int pl, val;
    };
    
    bool operator <(node x, node y) {
    	return x.val > y.val;
    }
    
    priority_queue <node> q;
    
    void Delete(int now) {//删掉它两边的点
    	a[a[a[now].l].l].r = now;
    	a[a[a[now].r].r].l = now;
    	a[now].l = a[a[now].l].l;
    	a[now].r = a[a[now].r].r;
    }
    
    int main() {
    	scanf("%d %d %d", &n, &k, &lst);
    	for (int i = 1; i < n; i++) {
    		scanf("%d", &x);
    		
    		a[i].l = i - 1;
    		a[i].r = i + 1;
    		a[i].val = x - lst;
    		q.push((node){i, a[i].val});
    		
    		lst = x;
    	}
    	
    	a[0].val = a[n].val = 1e9 + 1e7;//注意边界是要尽可能不优
    	for (int i = 1; i <= k; i++) {
    		while (cnot[q.top().pl]) q.pop();
    		
    		node now = q.top();
    		q.pop();
    		ans += now.val;
    		cnot[a[now.pl].l] = cnot[a[now.pl].r] = 1;
    		a[now.pl].val = a[a[now.pl].l].val + a[a[now.pl].r].val - a[now.pl].val;//反悔的花费
    		q.push((node){now.pl, a[now.pl].val});
    		Delete(now.pl);
    	}
    	printf("%lld", ans);
    	
    	return 0;
    }
    
  • 相关阅读:
    sqlserver中的锁与事务
    策略模式
    异步编程
    并行聚合操作
    EF中的自动追踪与代理
    C#6.0语法糖
    EF中使用SqlQuery进行参数化查询时抛出异常
    乐观并发
    为什么那么多公司不用 .NET
    sqlserver 更改跟踪相关知识
  • 原文地址:https://www.cnblogs.com/Sakura-TJH/p/luogu_P3620.html
Copyright © 2020-2023  润新知