Codeforces Good Bye 2017 F - New Year and Rainbow Roads

本文介绍了一种基于贪心策略的颜色点匹配算法,通过不同颜色的点(R、B、G)来构建最优路径,最小化连接成本。讨论了G颜色点在不同位置时的各种情形及其对应的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、如果升序中没有G颜色点:
那么我们找到第一个B颜色点prb(prev-blue)和最后一个B颜色点sub(succ-blue),和第一个R颜色点prr(prev-red)和最后一个R颜色点suc(succ-red),最优的方案是:

cost=xsubxprb+xsurxprr

如果某个颜色也不存在,则记Δx=0。

二、考虑升序中G颜色点对图的划分:
无非几种情况:

[...]G[...]GG[]G[...]

①第一个G前还有其他点,比如说这样。
R1B1B2R2B3.........RiBkG

那么我们这么连接即可:
R1R2...B1B2...Ri|G|Bk

②两个G颜色点之间有其他元素
G1R1B1B2R2.........RiBkG2

那么我们有两种连接的方法:
Ⅰ.
R1|G1|B1......Ri|G2|Bk

R与R相连,B与B相连
Ⅱ.
R1|G1|B1...RrRr+1......BbBb+1...Ri|G2|Bk

将中间某两个点之间的边去掉,并把G1和G2连起来。
我们把最大的边剪掉。
对于Ⅰ和Ⅱ两种情况,取较小的cost即可。
③两个G颜色点之间没有其他元素
将G1和G2连接即可。
④最后一个G后还有其他点,仿照①即可。
上面基本上就是全部的思路,基于贪心。
#include <cstdio>
#include <queue>
using namespace std;
queue<int> is,b;char geto[5];
int m[300005];bool mb[300005],nb=true,nr=true,fb,fr;
int main(){
    int i=0,n,pre,suc,sub,sur,prb,prr;double cost=0,mxb,mxr;
    scanf("%d",&n);
    for(int j=0;j<n;j++){
        scanf("%d%s",&m[j],geto);
        if(geto[0]=='G')is.push(j);
        if(geto[0]=='B')mb[j]=true;
    }
    if(is.empty()){
        prb=prr=0;sub=sur=n-1;
        while(prb<n){if(mb[prb])break;prb++;}
        while(prr<n){if(!mb[prr])break;prr++;}
        if(prb!=n-1)while(sub>=0){if(mb[sub])break;sub--;}
        if(prr!=n-1)while(sur>=0){if(!mb[sur])break;sur--;}
        cost+=m[sub]-m[prb]+m[sur]-m[prr];
    }else{
        suc=is.front();is.pop();
        while(i<suc){
            if(mb[i]&&nb)cost+=m[suc]-m[i],nb=false;
            if(!mb[i]&&nr)cost+=m[suc]-m[i],nr=false;
            i++;
        }
        for(i++;i<n;i++){
            nb=nr=false;fb=fr=true;mxb=mxr=-1;
            pre=suc;if(!is.empty()){suc=is.front();is.pop();}

            if(pre==suc){
                i=n-1;
                while(i>pre){
                    if(mb[i]&&!nb)cost+=m[i]-m[pre],nb=true;
                    if(!mb[i]&&!nr)cost+=m[i]-m[pre],nr=true;
                    i--;
                }
                break;
            }else{
                sub=prb=sur=prr=pre;
                while(i<suc){
                    if(mb[i]){
                        sub=i;nb=true;
                        if(m[sub]-m[prb]>mxb)mxb=m[sub]-m[prb];
                        prb=sub;
                    }else{
                        sur=i;nr=true;
                        if(m[sur]-m[prr]>mxr)mxr=m[sur]-m[prr];
                        prr=sur;
                    }
                    i++;
                }
                if(nb||nr){
                    if(sub!=pre)mxb=max(mxb,(double)m[suc]-m[sub]);
                    if(sur!=pre)mxr=max(mxr,(double)m[suc]-m[sur]);
                    if(mxr==-1)cost+=((m[suc]-m[pre])<<1)-mxb;
                    else if(mxb==-1)cost+=((m[suc]-m[pre])<<1)-mxr;
                    else cost+=((m[suc]-m[pre])<<1)+min(0.0,m[suc]-m[pre]-mxb-mxr);
                }else cost+=m[suc]-m[pre];
            }
        }
    }
    printf("%.0lf",cost);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值