披着信息的皮的数学题

 

题目描述

您有一个字符串a1,a2,…,ana1,a2,…,an,由零和一组成。

我们将连续元素ai,ai + 1,...,ajai,ai +1,...,aj(1≤i≤j≤n1≤i≤j≤n)的序列称为字符串aa的子字符串。

您可以多次应用以下操作:

选择一个字符串aa的子字符串(例如,您可以选择整个字符串)并反转它,为此支付xx个硬币(例如,«0101101»→→«0111001»);
选择字符串aa的一些子字符串(例如,您可以选择整个字符串或仅一个符号),然后将每个符号替换为相反的一个符号(零替换为一个,而一个替换为零),为此支付yy个硬币(例如,«0101101»→→«0110001»)。
您可以按任何顺序应用这些操作。允许将操作多次应用于同一子字符串。

您需要花费最少的硬币数量才能得到仅包含一个的字符串?


 

输入值
输入的第一行包含整数nn,xx和yy(1≤n≤300000,0≤x,y≤1091≤n≤300000,0≤x,y≤109)—字符串的长度,第一次操作的成本(反向子串)和第二个操作的成本(反向子串的所有元素)。

第二行包含长度为nn的字符串aa,由零和一组成。

输出量
打印一个整数-获得只包含1的字符串所需的最低总操作成本。如果不需要执行任何操作,请打印00。


 

例子
输入值
5 1 10
01000
输出量
11


输入值
5 10 1
01000
输出量
2


输入值
7 2 3
1111111
输出量
0


 

注意
在第一个示例中,首先需要反转子字符串[1…2] [1…2],然后需要反转子字符串[2…5] [2…5]。

然后,字符串更改如下:

«01000»→→«10000»→→«11111»。

运营总成本为1 + 10 = 111 + 10 = 11。

在第二个示例中,首先需要反转子字符串[1…1] [1…1],然后需要反转子字符串[3…5] [3…5]。

然后,字符串更改如下:

«01000»→→«11000»→→«11111»。

总成本为1 + 1 = 21 + 1 = 2。

在第三个示例中,字符串已经仅包含一个,因此答案为0。


思路分析:

  看这道题,脑子里立马就蹦出了DP,前两道题也都是DP,就坚信这道题得这么做了,然后就懵了

  这道题看起来简洁明了,就是一个只有0和1的字符串,然后对其进行两种操作:区间内前后翻转和把区间内0全变为1。然后求把整个字符串全变为1的最小代价。

  恶心的地方就在这两种操作都可以执行上了,有点无从下手。

  没办法,分析样例。不难发现,可以对数据进行分组,即把多个连在一起的0或1视为一个整体进行处理(显然这样可以尽可能少的处理,代价最小),然后对每组0进行考虑,这时候的字符串形式就是类似于0101010……这样的

  我们的最终目的是要把0变为1,你再怎么换,你也少不了变为0的操作——划重点,代码会体现

  我们考虑每两个相邻的0101,发现有两种方法处理:(1)转一下前面的01,变为1001;然后进行修改,变为1111;(2)进行两次修改,变为1111;

  我们发现,其中都有一次修改操作,那我们比较另一个不同的即可,显然谁代价小选哪个,不管你选哪个,都不需要再考虑这前面的一组,如果你变为1了,显然不用再管了,如果你选择了转换,那前面的0和后面的0就成一组0了,接下来再只考虑后面的就可以了。如果你着这样我们就合并了两个相邻的;

  接下来,数组还是101010……的形式,继续进行如下操作,每组01都是这样,那就简单了,如果有n组01,我们把最后一次转化为1的操作单独拿出来,剩下的n-1组我们只需进行(n-1)次的单一操作——两种操作哪个小选哪个。

配合代码理解(不懂一定要自己模拟一遍样例,体会一下过程):

 1 #include<cstdio>
 2 #include<cmath>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 const int maxn = 3e5+10;
 7 char a[maxn];
 8 long long tot;
 9 int main(){
10     long long n,x,y;scanf("%lld%lld%lld ",&n,&x,&y);
11     scanf("%s",a+1);
12     a[0] = '1';
13     for(int i = 1; i <= n;i++){
14         if(a[i] == '0' && a[i-1] == '1')tot++; //出现不相邻的0,就多了一组01,否则与前面的0为同一组
15     }
16     if(tot == 0)printf("0");
17     else printf("%lld",min(x,y) * (tot-1) + y); //前tot-1组,单一操作合并为一组,最后再加上一次统一修改的操作
18     return 0;
19 }

这就是一个数学题...

posted @ 2020-04-11 18:00  HH_Halo  阅读(171)  评论(0编辑  收藏  举报