Saving the City 思维

传送门:https://codeforces.ml/contest/1443/problem/B

题意

  给你一串01字符串,你可以花费a引爆一串连续的1,或者花费b让一个0变成1。问引爆这串字符串的所有1需要多少花费。

思路

  假设这串字符的1的联通块个数有num个,则他们之间0的联通块为num-1个

  若a<=b则ans为num*a,因为填补中间零加上引爆一次的花费至少为(num-1)*b+a,直接引爆的花费为num*a,因为a<=b故后者小。

  若a>b,我们则处理出每两个1直接的0的个数,从小往大遍历,设当前0的个数为x,若x*b+a<2*a,则填补这些0。

  故我们计算a的贡献次数和b的贡献次数,a原本贡献次数为num,每次填补都会贡献x次b,使a减少一次贡献,因为连着一起引爆了。

AC代码

#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;
const int maxn=1e5+5;
typedef long long ll;
int t,a,b,num1,len,flag;
char s[maxn];
int num[maxn];
ll ans;
int main()
{
    cin>>t;
    while(t--){
        cin>>a>>b;
        cin>>s+1;
        num1=0;len=strlen(s+1);
        flag=1;ans=0;
        for(int i=1;i<=len;i++){
            if(s[i]=='1'&&flag){
                num1++;
                flag=0;
            }
            if(s[i]=='0'){
                flag=1;
            }
        }
        if(a<=b){
            cout<<num1*a<<'\n';
            continue;
        }
        int k=0,now=0;flag=0;
        for(int i=1;i<=len;i++){    
            if(s[i]=='0'&&flag){
                now++;
            }
            if(s[i]=='1'&&now){
                num[++k]=now;
                now=0;
            }
            if(s[i]=='1') flag=1;
        }
        sort(num+1,num+1+k);
        int cnt=0;
        for(int i=1;i<=k;i++){
            if(num[i]*b+a<2*a){
                ans+=num[i];
                cnt++;
            }
            else break;
        }
        ans=ans*b+(num1-cnt)*a;
        cout<<ans<<'\n';
    }
}
/*
100
1 1
00110001100101
*/

 

posted @ 2020-11-03 12:10  艾尔夏尔-Layton  阅读(211)  评论(0编辑  收藏  举报