【习题总结】反悔贪心

前面的话:

遗物。

反悔贪心

SoyTony 教育我们要写反悔贪心

SoyTony 瑞平:

“什么叫 SoyTony 教育你要做反悔贪心?SoyTony 教育你这个?就当是教育了吧。"

反悔贪心题单

模型:

至少这些题里只有两个模型:

  • 一种靠链表维护节点间的相邻关系,限制是相邻的两点只能选一个。
  • 另一种靠若干个堆维护反悔机制,限制是一个点有多个不同的属性,只能选择一种属性产生贡献。

反悔机制:

模型一:

模型一的限制在于位置关系,比如相邻的点不能选,可以想到用双向链表来维护点之间的位置关系。由于选最大的点不一定最优,它两边的点加起来可能更优。所以当我们取一个点后,再加入一个点,这个点的权值是左边的点权 \(+\) 右边的点权 \(-\) 自身的点权。

模型二:

模型二的限制在于同一个点只有一个属性能产生贡献。可以用若干个堆来维护这些属性的转移关系,再相关的分类讨论,进行转移。

题:

P1484 种树

典型的模型一,两个相邻的坑不会都种树,用链表维护坑与坑之间的相邻关系,用大根堆维护。注意边界条件。

Code
#include<queue>
#include<vector>
#include<cstdio>
#include<algorithm>

#define LL long long

using namespace std;

const int MAXN = 5e5 + 10;
const int INF = 998244353;
int n, k;
LL sum, ans;
bool vis[MAXN];

struct List{
    int last, next;
}l[MAXN];

struct Place{
    LL val, pos;

    bool operator < (const Place &b) const{
        return val < b.val;
    }
}p[MAXN];

priority_queue< Place, vector<Place>, less<Place> > q;

inline LL read(){
    LL x = 0, f = 1;
    char c = getchar();

    while(c < '0' || c > '9'){
        if(c == '-') f = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9'){
        x = (x << 1) + (x << 3) + (c ^ 48);
        c = getchar();
    }
    
    return x * f;
}

int main(){
    n = read(), k = read();
    for(register int i = 1; i <= n; i++){
        p[i].val = read(), p[i].pos = i;
        q.push(p[i]);
    }

    p[0] = (Place){-INF, 0};
    p[n + 1] = (Place){-INF, n + 1};
    q.push(p[0]), q.push(p[n + 1]);
    
    l[0].next = 1, l[n + 1].last = n;
    for(register int i = 1; i <= n; i++)
        l[i].last = i - 1, l[i].next = i + 1;
    
    for(register int i = 1; i <= k; i++){
        while(!q.empty() && vis[q.top().pos]) q.pop();

        Place t = q.top();
        q.pop();

        sum += t.val;
        int u = l[t.pos].last, v = l[t.pos].next;
        vis[u] = vis[v] = true;
        p[t.pos].val = p[u].val + p[v].val - t.val;
        l[t.pos].last = l[u].last, l[t.pos].next = l[v].next;
        l[l[u].last].next = t.pos, l[l[v].next].last = t.pos;
        q.push(p[t.pos]);

        ans = max(ans, sum);
    }

    printf("%lld", ans);

    return 0;
}

P3620 [APIO/CTSC2007] 数据备份

显然,让两个相邻的大楼相连是最优的,每栋大楼只能连一根电缆,典型的返回贪心,依然是链表维护相邻关系,小根堆维护。

点击查看代码
#include<queue>
#include<vector>
#include<cstdio>
#include<algorithm>

#define LL long long

using namespace std;

const int MAXN = 1e5 + 10;
const int INF = 2147483647;
int n, k;
LL ans;
LL sit[MAXN];
bool vis[MAXN];

struct List{
    int last, next;
}l[MAXN];

struct Line{
    LL len, pos;

    bool operator > (const Line &b) const{
        return len > b.len;
    }
}b[MAXN];

priority_queue< Line, vector<Line>, greater<Line> > q;

inline int read(){
    int x = 0, f = 1;
    char c = getchar();

    while(c < '0' || c > '9'){
        if(c == '-') f = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9'){
        x = (x << 1) + (x << 3) + (c ^ 48);
        c = getchar();
    }

    return x * f;
}

int main(){
    n = read(), k = read();
    for(register int i = 1; i <= n; i++)
        sit[i] = read();
    
    b[0] = (Line){INF, 0};
    b[n] = (Line){INF, n}; 
    q.push(b[0]), q.push(b[n]);
    for(register int i = 1; i < n; i++){
        int st = sit[i], ed = sit[i + 1];
        b[i].len = ed - st, b[i].pos = i;
        q.push(b[i]);
    }

    for(register int i = 1; i <  n; i++)
        l[i].last = i - 1, l[i].next = i + 1;
    
    for(register int i = 1; i <= k; i++){
        while(!q.empty() && vis[q.top().pos]) q.pop();

        Line t = q.top();
        q.pop();

        ans += t.len;
        int u = l[t.pos].last, v = l[t.pos].next;
        vis[u] = vis[v] = true;
        b[t.pos].len = b[u].len + b[v].len - t.len;
        l[t.pos].last = l[u].last, l[t.pos].next = l[v].next;
        l[l[u].last].next = t.pos, l[l[v].next].last = t.pos;
        q.push(b[t.pos]);
    }

    printf("%lld", ans);

    return 0;
}

CF730I Olympiad in Programming and Sports

套用模型二,我们先把前 \(p\) 大的丢进编程团队,剩下的有两种情况:

  • 将一个没选过的人加入运动团队,价值 \(b_i\)
  • 将一个选过的人加入运动团队,再找一个人加入编程团队,价值 \(b_i - a_i + a_j\)

开三个堆维护即可。

Code
#include<queue>
#include<vector>
#include<cstdio>
#include<algorithm>

using namespace std;

const int MAXN = 3010;
int n, p, s, sum;
int bel[MAXN];

struct Student{
    int cs, pe, cut, id;
}a[MAXN];

struct Cmp1{
    bool operator () (const Student &a, const Student &b) const{
        return a.cs < b.cs;
    }
};

struct Cmp2{
    bool operator () (const Student &a, const Student &b) const{
        return a.pe < b.pe;
    }
};

struct Cmp3{
    bool operator () (const Student &a, const Student &b) const{
        return a.cut < b.cut;
    }
};

priority_queue< Student, vector< Student >, Cmp1 > q1;
priority_queue< Student, vector< Student >, Cmp2 > q2;
priority_queue< Student, vector< Student >, Cmp3 > q3;

inline int read(){
    int x = 0, f = 1;
    char c = getchar();

    while(c < '0' || c > '9'){
        if(c == '-') f = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9'){
        x = (x << 1) + (x << 3) + (c ^ 48);
        c = getchar();
    }

    return x * f;
}

int main(){
    n = read(), p = read(), s = read();
    for(register int i = 1; i <= n; i++) a[i].cs = read();
    for(register int i = 1; i <= n; i++) a[i].pe = read();

    for(register int i = 1; i <= n; i++){
        a[i].id = i, a[i].cut = a[i].pe - a[i].cs;
        q1.push(a[i]);
    }
    for(register int i = 1; i <= p; i++){
        Student t = q1.top(); q1.pop();
        sum += t.cs, bel[t.id] = 1;
    }
    for(register int i = 1; i <= n; i++){
        if(!bel[i]) q2.push(a[i]);
        else q3.push(a[i]);
    }

    for(register int i = 1; i <= s; i++){
        while(!q1.empty() && bel[q1.top().id]) q1.pop();
        while(!q2.empty() && bel[q2.top().id]) q2.pop();
        Student t1 = q1.top(); q1.pop();
        Student t2 = q2.top(); q2.pop();
        Student t3 = q3.top(); q3.pop();

        if(t3.cut + t1.cs > t2.pe){
            sum += t3.cut + t1.cs;
            bel[t1.id] = 1, bel[t3.id] = 2;
            q2.push(t2), q3.push(a[t1.id]);
        }
        else{
            sum += t2.pe;
            bel[t2.id] = 2;
            q1.push(t1), q3.push(t3);
        }
    }

    printf("%d\n", sum);
    for(register int i = 1; i <= n; i++) if(bel[i] == 1) printf("%d ", i);
    puts("");
    for(register int i = 1; i <= n; i++) if(bel[i] == 2) printf("%d ", i);

    return 0;
}
posted @ 2022-11-25 19:17  TSTYFST  阅读(95)  评论(3编辑  收藏  举报