传送门: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<<' '; 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<<' '; } } /* 100 1 1 00110001100101 */