题目地址:

https://www.luogu.com.cn/problem/P2708

题目描述:
有很多个硬币摆在一行,有正面朝上的,也有背面朝上的。正面朝上的用111表示,背面朝上的用000表示。现在要求从这行的第一个硬币开始,将从第一个硬币开始的前若干个硬币同时翻面,求如果要将所有硬币翻到正面朝上,最少要进行这样的操作多少次?

输入格式:
一个字符串,由000和111组成,表示硬币状态。

输出格式:
一个整数,表示要翻转的最少次数。

代码如下:

#include <iostream>
#include <string>
using namespace std;int main() {string s;cin >> s;while (s.back() == '1') s.pop_back();if (!s.length()) {puts("0");return 0;}// 事实上下面的循环在求,去掉s末尾所有的1删除后,// 继续删除中间重复的连续的0或1,只保留一个,此时s的长度即为答案int t = 1;for (int i = 1; i < s.length(); i++)if (s[i] != s[i - 1])t++;cout << t << endl;return 0;
}

时间复杂度O(n)O(n)O(n)。

算法正确性证明:
设f(c1,c2,...,cn)f(c_1,c_2,...,c_n)f(c1​,c2​,...,cn​)为对于硬币序列(c1,...,cn)(c_1,...,c_n)(c1​,...,cn​)所需翻转的最少次数。
先证明:若cn=1c_n=1cn​=1,f(c1,c2,...,cn)=f(c1,c2,...,cn−1)f(c_1,c_2,...,c_n)=f(c_1,c_2,...,c_{n-1})f(c1​,c2​,...,cn​)=f(c1​,c2​,...,cn−1​)。因为左右序列前n−1n-1n−1个硬币状态相同,而左边多一个硬币,所以对于任意一个使得左边序列成为全朝上的操作序列q1q2...qsq_1q_2...q_sq1​q2​...qs​,将其中翻转111 ~ nnn的操作都置换为翻转111 ~ n−1n-1n−1,得到新的操作序列q1′q2′...qs′q'_1q'_2...q'_sq1′​q2′​...qs′​,此序列就可以将右边的硬币序列翻为全朝上,所以f(c1,c2,...,cn)≥f(c1,c2,...,cn−1)f(c_1,c_2,...,c_n)\ge f(c_1,c_2,...,c_{n-1})f(c1​,c2​,...,cn​)≥f(c1​,c2​,...,cn−1​)。由于cn=1c_n=1cn​=1,所以任何一个把后者序列翻转为全朝上的操作,都可以将前者翻为全朝上,所以f(c1,c2,...,cn)≤f(c1,c2,...,cn−1)f(c_1,c_2,...,c_n)\le f(c_1,c_2,...,c_{n-1})f(c1​,c2​,...,cn​)≤f(c1​,c2​,...,cn−1​)。所以f(c1,c2,...,cn)=f(c1,c2,...,cn−1)f(c_1,c_2,...,c_n) = f(c_1,c_2,...,c_{n-1})f(c1​,c2​,...,cn​)=f(c1​,c2​,...,cn−1​)。既然如此,不妨设cn=0c_n=0cn​=0,也就是将sss末尾的111 pop掉。如果sss变为空,显然结果是000。

设cn=cn−1=...=cs=0≠cs−1=1c_n=c_{n-1}=...=c_s=0\ne c_{s-1}=1cn​=cn−1​=...=cs​=0​=cs−1​=1,由于末尾的背面朝上的硬币必然要翻,所以得到递推式f(c1,c2,...,cn)=f(¬c1,¬c2,...,¬cn)+1=f(¬c1,¬c2,...,¬cs−1)+1f(c_1,c_2,...,c_n) = f(\neg c_1,\neg c_2,...,\neg c_n) + 1=f(\neg c_1,\neg c_2,...,\neg c_{s-1}) + 1f(c1​,c2​,...,cn​)=f(¬c1​,¬c2​,...,¬cn​)+1=f(¬c1​,¬c2​,...,¬cs−1​)+1(此处需要证明每个操作的先后次序是可以交换的。这本质上其实是二进制nnn维向量加法的交换律,是显然成立的。所以不妨先进行翻转111到nnn的操作),这样把问题又变为末尾为111的情况。不停递推下去,直到序列缩小为空。显然,由证明过程可以知道,对于硬币序列里任何长度连续的111或000,都可以等效的看为只有111个。而对于(c1,c2,...,cn)=(1,0,1,0,...,n%2)(c_1,c_2,...,c_n)=(1,0,1,0,...,n\%2)(c1​,c2​,...,cn​)=(1,0,1,0,...,n%2)或(0,1,0,1,...,(n+1)%2)(0,1,0,1,...,(n+1)\%2)(0,1,0,1,...,(n+1)%2),根据递推方程,f(1,0,1,0,...,n%2)=n−n%2f(1,0,1,0,...,n\%2)=n-n\%2f(1,0,1,0,...,n%2)=n−n%2,f(0,1,0,1,...,(n+1)%2)=n−(n+1)%2f(0,1,0,1,...,(n+1)\%2)=n-(n+1)\%2f(0,1,0,1,...,(n+1)%2)=n−(n+1)%2。

直观上理解,答案可以这样得到:先删除sss末尾的111,然后数有多少段连续的000和111。段数即为答案。

下面举一个例子以方便理解:
f(11001011)=f(110010)=1+f(001101)=1+f(00110)=2+f(11001)=2+f(1100)=3+f(0011)=3+f(00)=4+f(11)=4f(11001011)=f(110010)=1+f(001101)\\=1+ f(00110)=2+f(11001)=2+f(1100)\\=3+f(0011)=3+f(00)=4+f(11)=4f(11001011)=f(110010)=1+f(001101)=1+f(00110)=2+f(11001)=2+f(1100)=3+f(0011)=3+f(00)=4+f(11)=4容易看出来,110010111100101111001011去掉末尾的111后,有四段:11,00,1,011,00,1,011,00,1,0,答案就是444。

【洛谷】P2708 硬币翻转(配数学证明)相关推荐

  1. 洛谷 洛谷 P2708 硬币翻转(高端算法)

    //DAY3.B //题源:洛谷 P2708 硬币翻转 //原题链接:https://www.luogu.com.cn/problem/P2708 #include<stdio.h> #i ...

  2. 洛谷 P2708 硬币翻转

    P2708 硬币翻转 时间限制1.00s 内存限制125.00MB 题目描述 从前有很多个硬币摆在一行,有正面朝上的,也有背面朝上的.正面朝上的用1表示,背面朝上的用0表示.现在要求从这行的第一个硬币 ...

  3. 洛谷——P2708 硬币翻转

    https://www.luogu.org/problem/show?pid=2708#sub 题目背景 难度系数:☆☆☆☆☆(如果你看懂了) 题目描述 从前有很多个硬币摆在一行,有正面朝上的,也有背 ...

  4. 洛谷 P2708 硬币翻转 题解

    题目传送门 真如题面所说,难度系数:☆☆☆☆☆(如果你看懂了). 从后往前扫一次,如果a[i]==0&&a[i-1]==1那么将ans+2. 注意最后不要忘记开头if(a[0]=='0 ...

  5. 洛谷——P1146 硬币翻转

    P1146 硬币翻转 题目描述 在桌面上有一排硬币,共NN枚,每一枚硬币均为正面朝上.现在要把所有的硬币翻转成反面朝上,规则是每次可翻转任意N-1N−1枚硬币(正面向上的被翻转为反面向上,反之亦然). ...

  6. 洛谷1146 硬币翻转

    题目描述 在桌面上有一排硬币,共NN枚,每一枚硬币均为正面朝上.现在要把所有的硬币翻转成反面朝上,规则是每次可翻转任意N-1N−1枚硬币(正面向上的被翻转为反面向上,反之亦然).求一个最短的操作序列( ...

  7. P2708 硬币翻转——题解2020.10.11

    P2708 硬币翻转 思路分析 定义一个一维字符型数组 a[ ] 存放若干硬币的正反情况,'1' 表示正面,'2'表示反面: 进行一次操作允许将前 n 个硬币翻面,要使操作的次数最少,每次操作须只对正 ...

  8. 洛谷 P2001 硬币的面值 题解

    原题链接 P2001 硬币的面值 - 洛谷 | 计算机科学教育新生态 题目描述 小A有 n n n 枚硬币,现在要买一样不超过 m m m 元的商品,他不想得到找钱(多脏啊),同时又不想带太多的硬币, ...

  9. 【洛谷】P1150 Peter的烟(配数学证明)

    题目地址: https://www.luogu.com.cn/problem/P1150 题目描述: Peter有nnn根烟,他每吸完一根烟就把烟蒂保存起来,kkk(k>1k>1k> ...

最新文章

  1. java irowset行数,Java CloneableRecord.put方法代码示例
  2. 《OpenGL编程指南(原书第9版)》——导读
  3. canal原理的一些学习-1(canal的一些原理性介绍)
  4. 抽象类和接口类的区别
  5. redis出现过多command 慢查询slowlog出现command命令
  6. 【转】ABP源码分析二十九:ABP.MongoDb
  7. vlookup两个条件匹配_vlookup,你还是只会基础的单条件查找?
  8. PKU 1087 A Plug for UNIX
  9. Scratch案例——放烟花
  10. php paypal支付接口文档,php 实现PayPal支付
  11. php excel复选框,Element表格嵌入复选框以及单选框的方法介绍(代码示例)
  12. 给GridLayout设置字视图的大小和视图之间的距离
  13. android10及以上通话录音
  14. cc美团_项目注册界面实现
  15. 线路负载及故障检测装置(2019全国大学生电子设计大赛C题:国家级一等奖)
  16. 事件委托、动画、插件
  17. js过滤对象中值为空的属性
  18. 区块链溯源相比传统追溯有什么优点?
  19. php免费利用飞信发送验证码,php通过飞信发送短信
  20. WikiTaxi_Importer_1.3.1 维基 离线数据库

热门文章

  1. 判断三个老师教哪门课命题c语言,语文教学论复习资料
  2. 闲置计费 | 冷启动与成本间的最优解
  3. C++ 使用 openGL 实现吃豆人游戏——项目实践(一)
  4. 乾坤大挪移,如何将同步阻塞(sync)三方库包转换为异步非阻塞(async)模式?Python3.10实现。
  5. MX Player 1.8.6第三方解码器,支持AC3、DTS
  6. 小程序源码:2022全新超火超热门模板的姓氏头像制作生成微信小程序源码-多玩法安装简单
  7. 控件必须放在RUNAT=”SERVER”的窗体内的解决方法
  8. 月薪30k 的Java面试题,哭着也要背完!(附答案)
  9. ArcGIS API For Javascript 4.15 绘制地图:结合 Echarts 绘制地形剖面图
  10. 石家庄外国语学校计算机,热烈祝贺石家庄外国语学校吴雨霏同学被美国麻省理工学院MIT录取!全国唯一的女生!...