帮助Bsny

题目描述

Bsny的书架乱成一团了,帮他一下吧!

他的书架上一共有n本书,我们定义混乱值是连续相同高度书本的段数。例如,如果书的高度是30,30,31,31,32,那么混乱值为3;30,32,32,31的混乱值也为3。但是31,32,31,32,31的混乱值为5,这实在是太乱了。

Bsny想尽可能减少混乱值,但他有点累了,所以他决定最多取出k本书,再随意将它们放回到书架上。你能帮助他吗?

输入

第一行两个整数n,k,分别表示书的数目和可以取出的书本数目。

接下来一行n个整数表示每本书的高度。

输出

仅一行一个整数,表示能够得到的最小混乱值。

样例输入

5 1
25 26 25 26 25

样例输出

3

提示

20%的数据:1≤n≤20,k=1。

40%的数据:书的高度不是25就是32,高度种类最多2种。

100%的数据:1≤k≤n≤100,注意所有书本高度在[25,32]。

来源

NOIP2014八校联考Test2 Day2

solution:

这道题是两年前出的,已经有很多题解了。正解是DP,比较复杂的状态压缩动态规划,这里就不多讲了。我将要讲另一种方法(纯属瞎搞),虽然很难AC,但平均可以得90分,在比赛里是很值的(这种方法不怎么用想,很容易实现,得分效率高,在不会做的时候是个不错的方法)。看到n比较小,但2^n枚举每本书是否取出肯定不行,不过还不算太离谱。想想曾经zhw学长教的一种方法——模拟退火法,在这题里似乎可行。在状态确定的情况下,可以轻松地在O(n)的时间里算出混乱度,然后直接套模拟退火法的模板就好了。可是,这种方法不常用,以至于我把退火的概率公式忘记了……然后随便编了一个,大概的趋势有那么一点像,但不靠谱。
这是我练习赛是的代码,公式乱造,只有55分(还可以了)

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cmath>
  4. #include<algorithm>
  5. #include<cstdlib>
  6. #include<cstring>
  7. using namespace std;
  8. typedef long long ll;
  9. ll read(){
  10. ll ans=;
  11. char ch=getchar(),last=' ';
  12. while(ch>''||ch<''){
  13. last=ch;
  14. ch=getchar();
  15. }
  16. while(ch<=''&&ch>=''){
  17. ans=ans*+ch-'';
  18. ch=getchar();
  19. }
  20. if(last=='-')
  21. ans=-ans;
  22. return ans;
  23. }
  24. int n,m,a[],b[],d[],x,y,ans,sum,last,pre,tot;
  25. bool c[];
  26. double T;
  27. const double ze=1e-;
  28. int random(int x){
  29. unsigned int ans=x;
  30. ans*=;
  31. return ans;
  32. }
  33. int main(){
  34. //freopen("bb.in","r",stdin);
  35. n=read();
  36. m=read();
  37. for(int i=;i<=n;i++)
  38. a[i]=read(),b[a[i]]++;
  39. T=;
  40. ans=n;
  41. for(int i=;i<=m;i++)
  42. c[i]=true,b[a[i]]--,d[a[i]]++;
  43. if(n==m){
  44. ans=;
  45. for(int i=;i<=;i++)
  46. if(d[i])
  47. ans++;
  48. printf("%d\n",ans);
  49. return ;
  50. }
  51. while(T>ze){
  52. //tot++;
  53. x=rand()%n+;
  54. while(c[x])
  55. x=rand()%n+;
  56. y=rand()%n+;
  57. while(!c[y])
  58. y=rand()%n+;
  59. c[x]=true;
  60. c[y]=false;
  61. b[a[x]]--;
  62. b[a[y]]++;
  63. d[a[x]]++;
  64. d[a[y]]--;
  65. sum=;
  66. last=;
  67. while(c[last])
  68. last++;
  69. if(last<=n){
  70. sum=;
  71. pre=last;
  72. for(int i=last+;i<=n;i++)
  73. if(!c[i]&&a[pre]!=a[i]){
  74. sum++;
  75. pre=i;
  76. }
  77. }
  78. for(int i=;i<=;i++)
  79. if(d[i]&&!b[i])
  80. sum++;
  81. if(ans>sum)
  82. ans=sum;
  83. else{
  84. if(rand()%+>log(T+)){
  85. c[x]=false;
  86. c[y]=true;
  87. b[a[x]]++;
  88. b[a[y]]--;
  89. d[a[x]]--;
  90. d[a[y]]++;
  91. }
  92. }
  93. T*=0.99998;
  94. }
  95. printf("%d\n",ans);
  96. //printf("%d %d\n",ans,tot);
  97. return ;
  98. }

赛后百度了一下正宗的模拟退火法,把代码修改一下,是这样的(90分,差不多了)

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cmath>
  4. #include<algorithm>
  5. #include<cstdlib>
  6. #include<cstring>
  7. using namespace std;
  8. typedef long long ll;
  9. ll read(){
  10. ll ans=;
  11. char ch=getchar(),last=' ';
  12. while(ch>''||ch<''){
  13. last=ch;
  14. ch=getchar();
  15. }
  16. while(ch<=''&&ch>=''){
  17. ans=ans*+ch-'';
  18. ch=getchar();
  19. }
  20. if(last=='-')
  21. ans=-ans;
  22. return ans;
  23. }
  24. int n,m,a[],b[],d[],x,y,ans,sum,last,pre,tot;
  25. bool c[];
  26. double T,de;
  27. const double ze=1e-;
  28. int main(){
  29. //freopen("bb.in","r",stdin);
  30. n=read();
  31. m=read();
  32. for(int i=;i<=n;i++)
  33. a[i]=read(),b[a[i]]++;
  34. T=;
  35. ans=n;
  36. for(int i=;i<=m;i++)
  37. c[i]=true,b[a[i]]--,d[a[i]]++;
  38. if(n==m){
  39. ans=;
  40. for(int i=;i<=;i++)
  41. if(d[i])
  42. ans++;
  43. printf("%d\n",ans);
  44. return ;
  45. }
  46. if(m==){
  47. printf("%d\n",n);
  48. return ;
  49. }
  50. while(T>ze){
  51. //tot++;
  52. x=rand()%n+;
  53. while(c[x])
  54. x=rand()%n+;
  55. y=rand()%n+;
  56. while(!c[y])
  57. y=rand()%n+;
  58. c[x]=true;
  59. c[y]=false;
  60. b[a[x]]--;
  61. b[a[y]]++;
  62. d[a[x]]++;
  63. d[a[y]]--;
  64. sum=;
  65. last=;
  66. while(c[last])
  67. last++;
  68. if(last<=n){
  69. sum=;
  70. pre=last;
  71. for(int i=last+;i<=n;i++)
  72. if(!c[i]&&a[pre]!=a[i]){
  73. sum++;
  74. pre=i;
  75. }
  76. }
  77. for(int i=;i<=;i++)
  78. if(d[i]&&!b[i])
  79. sum++;
  80. if(ans>sum)
  81. ans=sum;
  82. else{
  83. de=sum-ans;
  84. if((1.0/exp(de/T))*<=rand()%+){
  85. c[x]=false;
  86. c[y]=true;
  87. b[a[x]]++;
  88. b[a[y]]--;
  89. d[a[x]]--;
  90. d[a[y]]++;
  91. }
  92. }
  93. T*=0.99998;
  94. }
  95. printf("%d\n",ans);
  96. //printf("%d %d\n",ans,tot);
  97. return ;
  98. }

可惜这样得不了ac,然后ctime不能用,srand()也没办法,怎么办?手动改随机种子
这个95分

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cmath>
  4. #include<algorithm>
  5. #include<cstdlib>
  6. #include<cstring>
  7. using namespace std;
  8. typedef long long ll;
  9. ll read(){
  10. ll ans=;
  11. char ch=getchar(),last=' ';
  12. while(ch>''||ch<''){
  13. last=ch;
  14. ch=getchar();
  15. }
  16. while(ch<=''&&ch>=''){
  17. ans=ans*+ch-'';
  18. ch=getchar();
  19. }
  20. if(last=='-')
  21. ans=-ans;
  22. return ans;
  23. }
  24. int n,m,a[],b[],d[],x,y,ans,sum,last,pre,tot;
  25. bool c[];
  26. double T,de;
  27. const double ze=1e-;
  28. int main(){
  29. //freopen("bb.in","r",stdin);
  30. for(int i=;i<=;i++)
  31. n=rand();
  32. n=read();
  33. m=read();
  34. for(int i=;i<=n;i++)
  35. a[i]=read(),b[a[i]]++;
  36. T=;
  37. ans=n;
  38. for(int i=;i<=m;i++)
  39. c[i]=true,b[a[i]]--,d[a[i]]++;
  40. if(n==m){
  41. ans=;
  42. for(int i=;i<=;i++)
  43. if(d[i])
  44. ans++;
  45. printf("%d\n",ans);
  46. return ;
  47. }
  48. if(m==){
  49. printf("%d\n",n);
  50. return ;
  51. }
  52. while(T>ze){
  53. //tot++;
  54. x=rand()%n+;
  55. while(c[x])
  56. x=rand()%n+;
  57. y=rand()%n+;
  58. while(!c[y])
  59. y=rand()%n+;
  60. c[x]=true;
  61. c[y]=false;
  62. b[a[x]]--;
  63. b[a[y]]++;
  64. d[a[x]]++;
  65. d[a[y]]--;
  66. sum=;
  67. last=;
  68. while(c[last])
  69. last++;
  70. if(last<=n){
  71. sum=;
  72. pre=last;
  73. for(int i=last+;i<=n;i++)
  74. if(!c[i]&&a[pre]!=a[i]){
  75. sum++;
  76. pre=i;
  77. }
  78. }
  79. for(int i=;i<=;i++)
  80. if(d[i]&&!b[i])
  81. sum++;
  82. if(ans>sum)
  83. ans=sum;
  84. else{
  85. de=sum-ans;
  86. if((1.0/exp(de/T))*<=rand()%+){
  87. c[x]=false;
  88. c[y]=true;
  89. b[a[x]]++;
  90. b[a[y]]--;
  91. d[a[x]]--;
  92. d[a[y]]++;
  93. }
  94. }
  95. T*=0.99998;
  96. }
  97. printf("%d\n",ans);
  98. //printf("%d %d\n",ans,tot);
  99. //printf("%.6lf\n",exp(2));
  100. return ;
  101. }

难道到极限了吗?当然没有,一定可以ac的,我调了半天一直WA,同学看了一下,只改了一个字符,就ac了(强)。
下面是ac的代码

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cmath>
  4. #include<algorithm>
  5. #include<cstdlib>
  6. #include<cstring>
  7. using namespace std;
  8. typedef long long ll;
  9. ll read(){
  10. ll ans=;
  11. char ch=getchar(),last=' ';
  12. while(ch>''||ch<''){
  13. last=ch;
  14. ch=getchar();
  15. }
  16. while(ch<=''&&ch>=''){
  17. ans=ans*+ch-'';
  18. ch=getchar();
  19. }
  20. if(last=='-')
  21. ans=-ans;
  22. return ans;
  23. }
  24. int n,m,a[],b[],d[],x,y,ans,sum,last,pre,tot;
  25. bool c[];
  26. double T,de;
  27. const double ze=1e-;
  28. int main(){
  29. //freopen("bb.in","r",stdin);
  30. for(int i=;i<=;i++)
  31. n=rand();
  32. n=read();
  33. m=read();
  34. for(int i=;i<=n;i++)
  35. a[i]=read(),b[a[i]]++;
  36. T=;
  37. ans=n;
  38. for(int i=;i<=m;i++)
  39. c[i]=true,b[a[i]]--,d[a[i]]++;
  40. if(n==m){
  41. ans=;
  42. for(int i=;i<=;i++)
  43. if(d[i])
  44. ans++;
  45. printf("%d\n",ans);
  46. return ;
  47. }
  48. if(m==){
  49. printf("%d\n",n);
  50. return ;
  51. }
  52. while(T>ze){
  53. tot++;
  54. x=rand()%n+;
  55. while(c[x])
  56. x=rand()%n+;
  57. y=rand()%n+;
  58. while(!c[y])
  59. y=rand()%n+;
  60. c[x]=true;
  61. c[y]=false;
  62. b[a[x]]--;
  63. b[a[y]]++;
  64. d[a[x]]++;
  65. d[a[y]]--;
  66. sum=;
  67. last=;
  68. while(c[last])
  69. last++;
  70. if(last<=n){
  71. sum=;
  72. pre=last;
  73. for(int i=last+;i<=n;i++)
  74. if(!c[i]&&a[pre]!=a[i]){
  75. sum++;
  76. pre=i;
  77. }
  78. }
  79. for(int i=;i<=;i++)
  80. if(d[i]&&!b[i])
  81. sum++;
  82. if(ans>sum)
  83. ans=sum;
  84. else{
  85. de=sum-ans;
  86. if((1.0/exp(de/T))*<=(double)(rand()%+)){
  87. c[x]=false;
  88. c[y]=true;
  89. b[a[x]]++;
  90. b[a[y]]--;
  91. d[a[x]]--;
  92. d[a[y]]++;
  93. }
  94. }
  95. T*=0.999978;
  96. }
  97. printf("%d\n",ans);
  98. //printf("%d %d\n",ans,tot);
  99. return ;
  100. }

调随机种子是很傻的做法(比赛时不可能完成),调参数才更有效。
同样AC,不要调随机种子,在退火概率中,还有一个系数是可以改的。

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cmath>
  4. #include<algorithm>
  5. #include<cstdlib>
  6. #include<cstring>
  7. using namespace std;
  8. typedef long long ll;
  9. ll read(){
  10. ll ans=;
  11. char ch=getchar(),last=' ';
  12. while(ch>''||ch<''){
  13. last=ch;
  14. ch=getchar();
  15. }
  16. while(ch<=''&&ch>=''){
  17. ans=ans*+ch-'';
  18. ch=getchar();
  19. }
  20. if(last=='-')
  21. ans=-ans;
  22. return ans;
  23. }
  24. int n,m,a[],b[],d[],x,y,ans,sum,last,pre,tot;
  25. bool c[];
  26. double T,de;
  27. const double ze=1e-;
  28. int main(){
  29. n=read();
  30. m=read();
  31. for(int i=;i<=n;i++)
  32. a[i]=read(),b[a[i]]++;
  33. T=;
  34. ans=n;
  35. for(int i=;i<=m;i++)
  36. c[i]=true,b[a[i]]--,d[a[i]]++;
  37. if(n==m){
  38. ans=;
  39. for(int i=;i<=;i++)
  40. if(d[i])
  41. ans++;
  42. printf("%d\n",ans);
  43. return ;
  44. }
  45. if(m==){
  46. printf("%d\n",n);
  47. return ;
  48. }
  49. while(T>ze){
  50. tot++;
  51. x=rand()%n+;
  52. while(c[x])
  53. x=rand()%n+;
  54. y=rand()%n+;
  55. while(!c[y])
  56. y=rand()%n+;
  57. c[x]=true;
  58. c[y]=false;
  59. b[a[x]]--;
  60. b[a[y]]++;
  61. d[a[x]]++;
  62. d[a[y]]--;
  63. sum=;
  64. last=;
  65. while(c[last])
  66. last++;
  67. if(last<=n){
  68. sum=;
  69. pre=last;
  70. for(int i=last+;i<=n;i++)
  71. if(!c[i]&&a[pre]!=a[i]){
  72. sum++;
  73. pre=i;
  74. }
  75. }
  76. for(int i=;i<=;i++)
  77. if(d[i]&&!b[i])
  78. sum++;
  79. if(ans>sum)
  80. ans=sum;
  81. else{
  82. de=sum-ans;
  83. if((1.0/(exp(de/(T*1.2))))*<=(double)(rand()%+)){
  84. c[x]=false;
  85. c[y]=true;
  86. b[a[x]]++;
  87. b[a[y]]--;
  88. d[a[x]]--;
  89. d[a[y]]++;
  90. }
  91. }
  92. T*=0.999978;
  93. }
  94. printf("%d\n",ans);
  95. //printf("%d %d\n",ans,tot);
  96. return ;
  97. }

于是,这种费正解也把这道题ac了。如果书的高度种类有很多的话,这种方法可以比正解更好用,难道不是吗?

本片文章纯属乱搞,神犇不要喷……

帮助Bsny(乱搞做法)的更多相关文章

  1. BZOJ1278: 向量vector(计算几何 随机化乱搞)

    题意 题目链接 Sol 讲一下我的乱搞做法.... 首先我们可以按极角排序.然后对\(y\)轴上方/下方的加起来分别求模长取个最大值.. 这样一次是\(O(n)\)的. 我们可以对所有向量每次随机化旋 ...

  2. BZOJ2744:[HEOI2012]朋友圈(最大团,乱搞)

    Description 在很久很久以前,曾经有两个国家和睦相处,无忧无虑的生活着.一年一度的评比大会开始了,作为和平的两国,一个朋友圈数量最多的永远都是最值得他人的尊敬,所以现在就是需要你求朋友圈的最 ...

  3. [WC2018]通道(乱搞,迭代)

    [洛谷题面]https://www.luogu.org/problemnew/show/P4221 这个题以及[CTSC2018 暴力写挂]都有类似的乱搞做法能通过考场数据. 具体搞法就是随一个起点, ...

  4. 洛谷 P3438 - [POI2006]ZAB-Frogs(乱搞/李超线段树)

    题面传送门 首先一眼二分答案,我们假设距离 \((i,j)\) 最近的 scarefrog 离它的距离为 \(mn_{i,j}\),那么当我们二分到 \(mid\) 时我们显然只能经过 \(mn_{i ...

  5. Codeforces 306D - Polygon(随机化+乱搞)

    Codeforces 题目传送门 & 洛谷题目传送门 中考终于结束了--简单写道题恢复下状态罢. 首先这一类题目肯定没法用一般的方法解决,因此考虑用一些奇淫的乱搞做法解决这道题,不难发现,如果 ...

  6. 【BZOJ-3578】GTY的人类基因组计划2 set + map + Hash 乱搞

    3578: GTY的人类基因组计划2 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 367  Solved: 159[Submit][Status][ ...

  7. BZOJ_1800_[Ahoi2009]fly 飞行棋_乱搞

    BZOJ_1800_[Ahoi2009]fly 飞行棋_乱搞 Description 给出圆周上的若干个点,已知点与点之间的弧长,其值均为正整数,并依圆周顺序排列. 请找出这些点中有没有可以围成矩形的 ...

  8. CF809E Surprise me!(莫比乌斯反演+Dp(乱搞?))

    题目大意: 给你一棵树,树上的点编号为\(1-n\).选两个点\(i.j\),能得到的得分是\(\phi(a_i*a_j)*dis(i,j)\),其中\(dis(i,j)\)表示\(a\)到\(b\) ...

  9. hash进阶:使用字符串hash乱搞的姿势

    前言 此文主要介绍hash的各种乱搞方法,hash入门请参照我之前这篇文章 不好意思hash真的可以为所欲为 在开头先放一下题表(其实就是我题解中的hash题目qwq) 查询子串hash值 必备的入门 ...

随机推荐

  1. X Open Cup named after E.V. Pankratiev. European Grand Prix

    A. Arithmetic Rectangle 对于一行或者一列的情况可以递推求出最大值. 对于至少一行或者一列的情况,可以定义四个格子一组横向和纵向的相等关系,然后悬线法求最大子矩阵. 时间复杂度$ ...

  2. 根据文件大小自动判断单位B,KB,MB,GB

    <php> /** * 文件大小格式化 * @param integer $size 初始文件大小,单位为byte * @return array 格式化后的文件大小和单位数组,单位为by ...

  3. python中栈的单链表实现

    参考博客:https://www.cnblogs.com/stacklike/p/8284550.html 基于列表的简单实现 # 先进后出 # 以列表实现的简单栈 class SimpleStack ...

  4. 一般处理程序(ashx)的使用

    ASP.NET 中发送请求的页面代码如下: <head runat="server"> <title></title> <script s ...

  5. HBuilder

    什么是HBuilder? HBbuilder是DCloud(数字天堂)推出的一款支持HTML5的WEB开发IDE,主体是由java编写的,它将HTML/JS代码块进行代码封装,达到简单数据形成代码的特 ...

  6. 寻找真正的入口(OEP)--广义ESP定律

    1.前言 在论坛上看到很多朋友,不知道什么是ESP定律,ESP的适用范围是什么,ESP定律的原理是什么,如何使用ESP定律?看到了我在“”调查结果发现,大家对ESP定律很感兴趣,当然因为实在是太好用了 ...

  7. IIC稳定性.VBS

    Sub Main Dim cnt Dim delay Dim time Dim atttime atttime = 20 delay = 3000 time = 50 crt.screen.Send ...

  8. [Day16]常用API(正则表达式、Date类、DateFormat类、Calendar类)

    1.正则表达式(Regular Expression,regex)-是一个字符串,使用单个字符串来描述.用来定义匹配规则,匹配一系列符合某个句法规则的字符串 1.1匹配规则: (1)字符:x -代表的 ...

  9. ItunesConnect:"Missing Push Notification Entitlement"警告-----以及解决方法

    最近开发的cordova应用,要做ios的适配,并且发布版本,但是有一次在发测试版本的时候,突然收到一封邮件警告,原文如下: Missing Push Notification Entitlement ...

  10. TypeScript初探

    TypeScript初探 TypeScript什么? 官方给的定义:TypeScript是一种由微软开发的自由和开源的编程语言,它是JavaScript类型的超集,可以编译成纯JavaScript,本 ...