ybtoj「基础算法」第2章 贪心算法

A. 【例题1】奶牛晒衣服

显然 每次取湿度最大的衣服烘一定是最优的

而自然晾干对于每一件衣服相同时间晾干湿度相同 这不会影响衣服之间湿度的大小关系

所以开堆维护每一件衣服的湿度(不减去自然烘干部分)每次取湿度最大的一件 减去后放回堆里

每次取出湿度最大衣服 先考虑该衣服是否已自然烘干 如果干了其他的衣服一定也干了

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define inl inline 
const int N=1e5+10;
int n,a,b,h,ans;
priority_queue<int>q;
int main(){
    scanf("%d%d%d",&n,&a,&b);
    for(int i=1;i<=n;i++){scanf("%d",&h);q.push(h);}
    while(1){
        int x=q.top();q.pop();
        if(x-ans*a<=0)break;
        q.push(x-b);
        ans++;
    }
    printf("%d",ans);
    return 0;
}

B. 【例题2】雷达装置

首先 如果存在一个 \(y>d\) 那么一定无解

考虑对于每个建筑 雷达建在哪是合法的

image

如图 在 \([x-\sqrt{d^2-y^2},x+\sqrt{d^2-y^2}]\) 内需要有雷达

问题转化成在 \(n\) 个线段中找到最少的点数 使每个线段至少一个点

经典贪心 以右端点为关键字排序

每次放雷达时放在第一个不满足线段的右端点这样一定能覆盖到最多

具体看代码

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define inl inline 
const int N=1e3+5;
int n,d,x,y,ans;
double pst;
struct node{
    double l,r;
    friend bool operator<(node a,node b){
        return a.r<b.r;
    }
}a[N];
int main(){
    scanf("%d%d",&n,&d);
    for(int i=1;i<=n;i++){
        scanf("%d%d",&x,&y);
        if(d<y){puts("-1");return 0;}
        a[i].l=1.0*x-sqrt(d*d-y*y),a[i].r=1.0*x+sqrt(d*d-y*y);
        pst=min(pst,a[i].l-1);
    }
    sort(a+1,a+n+1);
    for(int i=1;i<=n;i++){
        if(pst>=a[i].l)continue;
        ans++;pst=a[i].r;
    }
    printf("%d",ans);
    return 0;
}

C. 【例题3】畜栏预定

把每个区间按左端点排序 开一个堆维护右端点的最早结束时间

每次看是否有空的牛棚 没有就开一个 模拟就行

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define inl inline 
const int N=1e5+5;
int n,cnt,res[N];
struct node{
    int l,r,id;
    friend bool operator<(node a,node b){
        return a.l<b.l;
    }
}a[N];
priority_queue<pair<int,int> >q;
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d%d",&a[i].l,&a[i].r),a[i].id=i;
    }
    sort(a+1,a+n+1);
    for(int i=1;i<=n;i++){
        if(q.empty()||-q.top().first>=a[i].l){res[a[i].id]=++cnt;q.push(make_pair(-a[i].r,cnt));continue;}
        res[a[i].id]=q.top().second;q.pop();q.push(make_pair(-a[i].r,res[a[i].id]));
    }
    printf("%d\n",cnt);
    for(int i=1;i<=n;i++)printf("%d\n",res[i]);
    return 0;
}

D. 【例题4】荷马史诗

其实是构造哈夫曼树的过程

就是下图:

image

每个单词就是根节点到所有叶节点边权合起来 比如5号节点就是01

所求答案即为 \(\sum dep_i\times w_i\)

显然 把 \(w_i\) 视为每个点权值 那么把权值大的点放上面一定更优

那么相当于先取 \(k\) 个权值小的点合并 然后放回去

看图(2叉树):

image

假如每个点编号代表权值 那么先选1 2

那么1 2就换成权值为3的点

image

那么下一步就是合并3 5

image

最后合并6 8

image

然鹅还有一个问题:k叉树可能不是满的 这就会导致根节点接不满 这样一定劣(与根节点相连的点深度小)

那么我们可以往里面加几个 \(w=0\) 的点 直到 \((n-1)\bmod (k-1)=0\)(n个叶子节点合并成一个 少了 \(n-1\) 个 每次合并减少 \(k-1\) 个点)

然后开堆维护权值最小的点即可

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inl inline 
const int N=1e5+5;
const int M=3e5+5;
int n,k,h;
ll sum,ans,w;
struct node{
    ll w;
    int h;
    friend bool operator<(node a,node b){return a.w==b.w?a.h>b.h:a.w>b.w;}
};
priority_queue<node>q;
signed main(){
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++){
        scanf("%lld",&w);
        q.push((node){w,1});
    }
    while((q.size()-1)%(k-1))q.push((node){0,1});
    while(q.size()>1){
        sum=h=0;
        for(int i=1;i<=k;i++){
            sum+=q.top().w;
            h=max(h,q.top().h);
            q.pop();
        }
        ans+=sum;
        q.push((node){sum,h+1});
    }
    printf("%lld\n%d",ans,q.top().h-1);
    return 0;
}
posted @ 2023-10-23 20:51  xiang_xiang  阅读(3)  评论(0编辑  收藏  举报