可撤销贪心 小专题

2 篇文章 0 订阅
2 篇文章 0 订阅

Warming up【JZOJ 4726】 种花

Description

圆环上有N个数,要选出两两不相邻的M个数,使得M个数权值和最大
M<=N<=200000

Analysis

把N个数丢进大根堆里,用链表维护左右位置,每次取堆顶?
这样明显有问题,有可能取堆顶非最优。
对于一个位置x,定义其左边位置为lx,右为rx
如果x不选,那么lx,rx都必定会选
所以如果我们要撤销x出的选择,就把-a[x]+a[lx]+a[rx]加入堆
这样进行了修正过的贪心就能保证正确性。


Exercise 【51nod 1380】夹克老爷的逢三抽一

几乎跟上面那题一模一样,此不再赘述


Further Challenge【JZOJ 5043】保持平衡

Description

博爱路上种起了一棵棵的大树,但是有一些地方的树超过了负荷,有一些地方的树的数量又不够。
我们不妨把博爱路看做一条数轴,数轴有n个点,从1到n编号,第i个位置原来现在有ai棵树,这个位置的需求是bi棵树。ai,bi都是0到10的整数。由于你需要是这个位置的树的数量保持平衡,所以你需要移除或者搬一些树过来。
我们怎么使树的数量平衡呢?
首先,你可以从某个位置i移动一棵树到位置j,这时,你需要的运费是|i-j|*z元。
其次,你可以从商店买一棵树,需要支付x元,这时商店会把树配送到任意位置。
还有就是,你可以叫别人收购在任意位置一棵树,需要支付y元运费。
三种操作的次数都没有限制
问使得树的数量平衡最小需要支付多少钱?
n<=100000

Analysis

位置可以发现只有两种:供给型(ai>bi),需求型(ai< bi)
由于ai,bi<=10,所以可以将一个位置拆成|ai-bi|个相同类型的位置,这样供给和需求都变成了1
此处只考虑供给位置
要么直接选择收购,要么选择移动
移动有两种:与前方的需求匹配,或者是当前不动与后方需求匹配
假设我们现在与前方需求匹配,那么肯定贪心选择一个费用最小的匹配,不会使得答案更差,使用一个需求堆来维护
但是,匹配后面的可能更优,所以我们要在供给堆里加入相应的值(为了以后撤销)

Code

#include<set>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,b,a) for(int i=b;i>=a;i--)
#define efo(i,v,u) for(int i=last[v],u=to[i];i;i=next[i],u=to[i])
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
#define mset(a,x) memset(a,x,sizeof(a))
using namespace std;
typedef long long ll;
typedef double db;
char ch;
void read(int &n)
{
    n=0;int p=1;
    for(ch=getchar();ch<'0' || ch>'9';ch=getchar())
        if(ch=='-') p=-1;
    for(;'0'<=ch && ch<='9';ch=getchar()) n=n*10+ch-'0';
    n*=p;
}
int n;
ll A,B,C;
multiset<ll> s[2];
int main()
{
    ll ans=0;
    read(n);scanf("%lld %lld %lld",&A,&B,&C);
    int x,y;
    fo(i,1,n)
    {
        read(x),read(y);
        fo(jy,1,x-y)//supply
        {
            ll co=B;
            if(!s[1].empty())
            {
                ll t=*s[1].begin();
                if(i*C+t<co)
                {
                    co=i*C+t;
                    s[1].erase(s[1].begin());
                }
            }
            s[0].insert(-i*C-co);
            ans+=co;
        }
        fo(jy,1,y-x)//demand
        {
            ll co=A;
            if(!s[0].empty())
            {
                ll t=*s[0].begin();
                if(i*C+t<co)
                {
                    co=i*C+t;
                    s[0].erase(s[0].begin());
                }
            }
            s[1].insert(-i*C-co);
            ans+=co;
        }
    }
    printf("%lld",ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值