Educational Codeforces Round 42 E 贪心

题目链接


题意:
数轴上有三种类型的点:B型点、R型点、P型点。
你可以连接任意两个点,连接的代价是两个点的距离,即坐标之差。
而最终希望达到的条件是:
如果去除所有B型点,R型点和P型点中任意两个点连通。
如果去除所有R型点,B型点和P型点中任意两个点连通。

现在给定n个点的坐标和类型,求满足条件的最小代价。


思路:

首先有一个显然合法的可行方案:
即先单独考虑所有B型点和P型点,相邻的两个点连边,最终代价为最右边的点(B/P)和最左边的点(B/P)的距离。
随后单独考虑所有R型点和P型点,分析同上。

可是考虑一个特殊情况,有四个点,从左到右的坐标及类型为:
P(0) 、 B(1) 、 R(99) 、 P(100)

对于上面的方案,代价显然是: $Ans = (100-0) + (100-0) = 200 $
而更好的选择是:将两个P点连接起来,随后B点连接左边的P点,R点连接右边的P点,此时代价为:
A n s = ( 100 − 0 ) + ( 1 − 0 ) + ( 100 − 99 ) = 102 Ans = (100-0) + (1-0) + (100-99) = 102 Ans=(1000)+(10)+(10099)=102

可以发现:
如果将两个相邻的P点连接起来,从而B型点和R型点连接时均可以共用这一条边,此时可能会让总代价变得更小。

故我们可以先按第一个方案算出一个初步的总代价 A n s Ans Ans
随后按坐标从左到右枚举两个相邻的P点:
而对于原来按第一种方案连接的结构,比如当考虑B型点和P型点时,连接是:
P 0 − B 0 − B 1 − B 2 − . . . − B t − P 1 P_0 - B_0 - B_1 - B_2 - ... - B_t - P_1 P0B0B1B2...BtP1

而此时因为 P 0 P_0 P0 P 1 P_1 P1已经连接,故中间有一条边可以断开不连,由贪心的思想显然断开的边越长越好,假设对于该情况,断开的边最长长度为 F u n ( B ) Fun(B) Fun(B)

同理,可以求出 F u n ( R ) Fun(R) Fun(R)

则对于两个相邻P点构成的子区间,此时需要付出的总代价为:(假设 L e n = P 1 − P 0 Len = P_1 - P_0 Len=P1P0)
S u m 1 = L e n + ( L e n − F u n ( B ) ) + ( L e n − F u n ( R ) ) = 3 L e n − F u n ( B ) − F u n ( R ) Sum1 = Len + (Len - Fun(B)) + (Len - Fun(R)) = 3Len - Fun(B) - Fun(R) Sum1=Len+(LenFun(B))+(LenFun(R))=3LenFun(B)Fun(R)

而原来需要付出的总代价为:
S u m 2 = L e n + L e n = 2 L e n Sum2 = Len + Len = 2Len Sum2=Len+Len=2Len

故对于原来得到的初步总代价 A n s Ans Ans,我们可以更新为:
A n s − = S u m 2 − S u m 1 Ans -= Sum2 - Sum1 Ans=Sum2Sum1

此题得解。


代码:

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;

char s[100+10];
vector<int> vR,vB,vP;

ll Fun(const vector<int>& v,int l,int r){
    int st = lower_bound(v.begin(),v.end(),l) - v.begin();
    int ed = lower_bound(v.begin(),v.end(),r) - v.begin();

    ll res = 0;
    for(int i=st ;i<ed ;i++) res = max(res,1LL*v[i+1]-1LL*v[i]);
    return res;
}

int main(){
    int n;scanf("%d",&n);

    for(int i=1 ;i<=n ;i++){
        int x;
        scanf("%d%s",&x,s);

        if(s[0] == 'R')         vR.push_back(x);
        else if(s[0] == 'B')    vB.push_back(x);
        else{
            vR.push_back(x);
            vB.push_back(x);
            vP.push_back(x);
        }
    }
    sort(vR.begin(),vR.end());
    sort(vB.begin(),vB.end());
    sort(vP.begin(),vP.end());

    ll ans = 0;
    if(vR.size() > 1) ans += vR[vR.size()-1] - vR[0];
    if(vB.size() > 1) ans += vB[vB.size()-1] - vB[0];

    int Siz = vP.size();
    for(int i=0 ;i<Siz-1 ;i++){
        int l = vP[i],r = vP[i+1];
        ans -= max(0LL,Fun(vB,l,r) + Fun(vR,l,r) - 1LL*(r-l));
    }
    printf("%I64d\n",ans);
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值