机房测试2:sushi(断环+贪心)

题目:

 分析:

因为原序列是一个环,所以要断环为链,将序列复制一份放在后面。

显然将R移动到一块的同时,B也会在一块,所以只需要求R移动到一起的贡献即可。

枚举一个分界点,让这个点左边所有的R都向左靠,右边所有的R都向右靠。这时候一定是满足题意的。

但会发现,同一个分界点,随着断环的位置改变,统计出来的答案也会改变,所以还要枚举一个断环点

复杂度是n^2

考虑优化:

RRBBRBBBR 像这样一组数据,把将R向两边放看做是将B往中间靠,那么在第5个位置是最优的(左边移两格,右边不需要移)。

当断环的点向右移,lr不会增加,rr不会减少,相当于如果分界点向左移动,只会给右边带来更多的花费,也不会给左边减少花费(因为移动的是最左边的点)。

所以断环的点不会从原来的位置向左移动。

显然我们不需要将分界点从头枚举到尾,我们只需要找到一个最大的位置,当答案不再减小,就break掉(也就是说答案随分界点的变化是单调的)。

复杂度:接近O(n)

实现流程:

定义lr为左边R的个数,rr为右边R的个数,r为当前所需花费。枚举断环点,枚举分界点,统计答案。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define N 1000005
#define ri register int
char s[N<<1];
void work()
{
    int n=strlen(s+1);
    for(ri i=1;i<=n;++i) s[i+n]=s[i];
    //断点指断环的点 分界点指不移动的点 将所有的B往中间分界点靠 R向两边靠 
    int lr=0,rr=0;
    ll r=0,ans;
    for(ri i=1;i<=n;++i) if(s[i]=='R') r+=n-i-rr,rr++;//预处理断点在1的情况
    //printf("%lld\n",rr);
    ans=r;
    int now=1;
    for(ri pos=1;pos<=n;++pos){//移动断点 
        while(now<n+pos){//枚举分界点 跳指针移动 
            if(s[now]=='R'){//移动B不影响 移动R会对lr 与 rr的统计有影响 
                ll tmp=r;
                rr--;//移到了一个R 所以先右边的-- 减去它自己 便于下面统计 注意分界点这个点是包括在右区间里面的 
                tmp+=now-pos-lr-(pos+n-1-now-rr);//pos是它目前的起点 pos+n-1是目前的终点 
                //统计步数:这个R本来有移到右边的贡献 现在减去 加上移到左边的贡献 
                lr++;//左边的++ 便于上面的统计 
                if(tmp<=r) r=tmp;//如果不能更新 就还原 然后break 
                else{ lr--; rr++; break; }
            }
            now++;
        }
        ans=min(ans,r);
        if(s[pos]=='R') lr--,rr++;//如果把R移过去了 右边的R++ 左边-- 
        else r+=rr-lr;//如果移动的是B 就要加上所有右边的R移动到右边的贡献 减去左边的R移动到左边的贡献
    }
    printf("%lld\n",ans);
}
int main()
{
    freopen("sushi.in","r",stdin);
    freopen("sushi.out","w",stdout);
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%s",s+1);
        work();
    }
}
/*
1
BBRBBRBBBRRR
*/

猜你喜欢

转载自www.cnblogs.com/mowanying/p/11622618.html