帮助Bsny(乱搞做法)
帮助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分(还可以了)
- #include<iostream>
- #include<cstdio>
- #include<cmath>
- #include<algorithm>
- #include<cstdlib>
- #include<cstring>
- using namespace std;
- typedef long long ll;
- ll read(){
- ll ans=;
- char ch=getchar(),last=' ';
- while(ch>''||ch<''){
- last=ch;
- ch=getchar();
- }
- while(ch<=''&&ch>=''){
- ans=ans*+ch-'';
- ch=getchar();
- }
- if(last=='-')
- ans=-ans;
- return ans;
- }
- int n,m,a[],b[],d[],x,y,ans,sum,last,pre,tot;
- bool c[];
- double T;
- const double ze=1e-;
- int random(int x){
- unsigned int ans=x;
- ans*=;
- return ans;
- }
- int main(){
- //freopen("bb.in","r",stdin);
- n=read();
- m=read();
- for(int i=;i<=n;i++)
- a[i]=read(),b[a[i]]++;
- T=;
- ans=n;
- for(int i=;i<=m;i++)
- c[i]=true,b[a[i]]--,d[a[i]]++;
- if(n==m){
- ans=;
- for(int i=;i<=;i++)
- if(d[i])
- ans++;
- printf("%d\n",ans);
- return ;
- }
- while(T>ze){
- //tot++;
- x=rand()%n+;
- while(c[x])
- x=rand()%n+;
- y=rand()%n+;
- while(!c[y])
- y=rand()%n+;
- c[x]=true;
- c[y]=false;
- b[a[x]]--;
- b[a[y]]++;
- d[a[x]]++;
- d[a[y]]--;
- sum=;
- last=;
- while(c[last])
- last++;
- if(last<=n){
- sum=;
- pre=last;
- for(int i=last+;i<=n;i++)
- if(!c[i]&&a[pre]!=a[i]){
- sum++;
- pre=i;
- }
- }
- for(int i=;i<=;i++)
- if(d[i]&&!b[i])
- sum++;
- if(ans>sum)
- ans=sum;
- else{
- if(rand()%+>log(T+)){
- c[x]=false;
- c[y]=true;
- b[a[x]]++;
- b[a[y]]--;
- d[a[x]]--;
- d[a[y]]++;
- }
- }
- T*=0.99998;
- }
- printf("%d\n",ans);
- //printf("%d %d\n",ans,tot);
- return ;
- }
赛后百度了一下正宗的模拟退火法,把代码修改一下,是这样的(90分,差不多了)
- #include<iostream>
- #include<cstdio>
- #include<cmath>
- #include<algorithm>
- #include<cstdlib>
- #include<cstring>
- using namespace std;
- typedef long long ll;
- ll read(){
- ll ans=;
- char ch=getchar(),last=' ';
- while(ch>''||ch<''){
- last=ch;
- ch=getchar();
- }
- while(ch<=''&&ch>=''){
- ans=ans*+ch-'';
- ch=getchar();
- }
- if(last=='-')
- ans=-ans;
- return ans;
- }
- int n,m,a[],b[],d[],x,y,ans,sum,last,pre,tot;
- bool c[];
- double T,de;
- const double ze=1e-;
- int main(){
- //freopen("bb.in","r",stdin);
- n=read();
- m=read();
- for(int i=;i<=n;i++)
- a[i]=read(),b[a[i]]++;
- T=;
- ans=n;
- for(int i=;i<=m;i++)
- c[i]=true,b[a[i]]--,d[a[i]]++;
- if(n==m){
- ans=;
- for(int i=;i<=;i++)
- if(d[i])
- ans++;
- printf("%d\n",ans);
- return ;
- }
- if(m==){
- printf("%d\n",n);
- return ;
- }
- while(T>ze){
- //tot++;
- x=rand()%n+;
- while(c[x])
- x=rand()%n+;
- y=rand()%n+;
- while(!c[y])
- y=rand()%n+;
- c[x]=true;
- c[y]=false;
- b[a[x]]--;
- b[a[y]]++;
- d[a[x]]++;
- d[a[y]]--;
- sum=;
- last=;
- while(c[last])
- last++;
- if(last<=n){
- sum=;
- pre=last;
- for(int i=last+;i<=n;i++)
- if(!c[i]&&a[pre]!=a[i]){
- sum++;
- pre=i;
- }
- }
- for(int i=;i<=;i++)
- if(d[i]&&!b[i])
- sum++;
- if(ans>sum)
- ans=sum;
- else{
- de=sum-ans;
- if((1.0/exp(de/T))*<=rand()%+){
- c[x]=false;
- c[y]=true;
- b[a[x]]++;
- b[a[y]]--;
- d[a[x]]--;
- d[a[y]]++;
- }
- }
- T*=0.99998;
- }
- printf("%d\n",ans);
- //printf("%d %d\n",ans,tot);
- return ;
- }
可惜这样得不了ac,然后ctime不能用,srand()也没办法,怎么办?手动改随机种子
这个95分
- #include<iostream>
- #include<cstdio>
- #include<cmath>
- #include<algorithm>
- #include<cstdlib>
- #include<cstring>
- using namespace std;
- typedef long long ll;
- ll read(){
- ll ans=;
- char ch=getchar(),last=' ';
- while(ch>''||ch<''){
- last=ch;
- ch=getchar();
- }
- while(ch<=''&&ch>=''){
- ans=ans*+ch-'';
- ch=getchar();
- }
- if(last=='-')
- ans=-ans;
- return ans;
- }
- int n,m,a[],b[],d[],x,y,ans,sum,last,pre,tot;
- bool c[];
- double T,de;
- const double ze=1e-;
- int main(){
- //freopen("bb.in","r",stdin);
- for(int i=;i<=;i++)
- n=rand();
- n=read();
- m=read();
- for(int i=;i<=n;i++)
- a[i]=read(),b[a[i]]++;
- T=;
- ans=n;
- for(int i=;i<=m;i++)
- c[i]=true,b[a[i]]--,d[a[i]]++;
- if(n==m){
- ans=;
- for(int i=;i<=;i++)
- if(d[i])
- ans++;
- printf("%d\n",ans);
- return ;
- }
- if(m==){
- printf("%d\n",n);
- return ;
- }
- while(T>ze){
- //tot++;
- x=rand()%n+;
- while(c[x])
- x=rand()%n+;
- y=rand()%n+;
- while(!c[y])
- y=rand()%n+;
- c[x]=true;
- c[y]=false;
- b[a[x]]--;
- b[a[y]]++;
- d[a[x]]++;
- d[a[y]]--;
- sum=;
- last=;
- while(c[last])
- last++;
- if(last<=n){
- sum=;
- pre=last;
- for(int i=last+;i<=n;i++)
- if(!c[i]&&a[pre]!=a[i]){
- sum++;
- pre=i;
- }
- }
- for(int i=;i<=;i++)
- if(d[i]&&!b[i])
- sum++;
- if(ans>sum)
- ans=sum;
- else{
- de=sum-ans;
- if((1.0/exp(de/T))*<=rand()%+){
- c[x]=false;
- c[y]=true;
- b[a[x]]++;
- b[a[y]]--;
- d[a[x]]--;
- d[a[y]]++;
- }
- }
- T*=0.99998;
- }
- printf("%d\n",ans);
- //printf("%d %d\n",ans,tot);
- //printf("%.6lf\n",exp(2));
- return ;
- }
难道到极限了吗?当然没有,一定可以ac的,我调了半天一直WA,同学看了一下,只改了一个字符,就ac了(强)。
下面是ac的代码
- #include<iostream>
- #include<cstdio>
- #include<cmath>
- #include<algorithm>
- #include<cstdlib>
- #include<cstring>
- using namespace std;
- typedef long long ll;
- ll read(){
- ll ans=;
- char ch=getchar(),last=' ';
- while(ch>''||ch<''){
- last=ch;
- ch=getchar();
- }
- while(ch<=''&&ch>=''){
- ans=ans*+ch-'';
- ch=getchar();
- }
- if(last=='-')
- ans=-ans;
- return ans;
- }
- int n,m,a[],b[],d[],x,y,ans,sum,last,pre,tot;
- bool c[];
- double T,de;
- const double ze=1e-;
- int main(){
- //freopen("bb.in","r",stdin);
- for(int i=;i<=;i++)
- n=rand();
- n=read();
- m=read();
- for(int i=;i<=n;i++)
- a[i]=read(),b[a[i]]++;
- T=;
- ans=n;
- for(int i=;i<=m;i++)
- c[i]=true,b[a[i]]--,d[a[i]]++;
- if(n==m){
- ans=;
- for(int i=;i<=;i++)
- if(d[i])
- ans++;
- printf("%d\n",ans);
- return ;
- }
- if(m==){
- printf("%d\n",n);
- return ;
- }
- while(T>ze){
- tot++;
- x=rand()%n+;
- while(c[x])
- x=rand()%n+;
- y=rand()%n+;
- while(!c[y])
- y=rand()%n+;
- c[x]=true;
- c[y]=false;
- b[a[x]]--;
- b[a[y]]++;
- d[a[x]]++;
- d[a[y]]--;
- sum=;
- last=;
- while(c[last])
- last++;
- if(last<=n){
- sum=;
- pre=last;
- for(int i=last+;i<=n;i++)
- if(!c[i]&&a[pre]!=a[i]){
- sum++;
- pre=i;
- }
- }
- for(int i=;i<=;i++)
- if(d[i]&&!b[i])
- sum++;
- if(ans>sum)
- ans=sum;
- else{
- de=sum-ans;
- if((1.0/exp(de/T))*<=(double)(rand()%+)){
- c[x]=false;
- c[y]=true;
- b[a[x]]++;
- b[a[y]]--;
- d[a[x]]--;
- d[a[y]]++;
- }
- }
- T*=0.999978;
- }
- printf("%d\n",ans);
- //printf("%d %d\n",ans,tot);
- return ;
- }
调随机种子是很傻的做法(比赛时不可能完成),调参数才更有效。
同样AC,不要调随机种子,在退火概率中,还有一个系数是可以改的。
- #include<iostream>
- #include<cstdio>
- #include<cmath>
- #include<algorithm>
- #include<cstdlib>
- #include<cstring>
- using namespace std;
- typedef long long ll;
- ll read(){
- ll ans=;
- char ch=getchar(),last=' ';
- while(ch>''||ch<''){
- last=ch;
- ch=getchar();
- }
- while(ch<=''&&ch>=''){
- ans=ans*+ch-'';
- ch=getchar();
- }
- if(last=='-')
- ans=-ans;
- return ans;
- }
- int n,m,a[],b[],d[],x,y,ans,sum,last,pre,tot;
- bool c[];
- double T,de;
- const double ze=1e-;
- int main(){
- n=read();
- m=read();
- for(int i=;i<=n;i++)
- a[i]=read(),b[a[i]]++;
- T=;
- ans=n;
- for(int i=;i<=m;i++)
- c[i]=true,b[a[i]]--,d[a[i]]++;
- if(n==m){
- ans=;
- for(int i=;i<=;i++)
- if(d[i])
- ans++;
- printf("%d\n",ans);
- return ;
- }
- if(m==){
- printf("%d\n",n);
- return ;
- }
- while(T>ze){
- tot++;
- x=rand()%n+;
- while(c[x])
- x=rand()%n+;
- y=rand()%n+;
- while(!c[y])
- y=rand()%n+;
- c[x]=true;
- c[y]=false;
- b[a[x]]--;
- b[a[y]]++;
- d[a[x]]++;
- d[a[y]]--;
- sum=;
- last=;
- while(c[last])
- last++;
- if(last<=n){
- sum=;
- pre=last;
- for(int i=last+;i<=n;i++)
- if(!c[i]&&a[pre]!=a[i]){
- sum++;
- pre=i;
- }
- }
- for(int i=;i<=;i++)
- if(d[i]&&!b[i])
- sum++;
- if(ans>sum)
- ans=sum;
- else{
- de=sum-ans;
- if((1.0/(exp(de/(T*1.2))))*<=(double)(rand()%+)){
- c[x]=false;
- c[y]=true;
- b[a[x]]++;
- b[a[y]]--;
- d[a[x]]--;
- d[a[y]]++;
- }
- }
- T*=0.999978;
- }
- printf("%d\n",ans);
- //printf("%d %d\n",ans,tot);
- return ;
- }
于是,这种费正解也把这道题ac了。如果书的高度种类有很多的话,这种方法可以比正解更好用,难道不是吗?
本片文章纯属乱搞,神犇不要喷……
帮助Bsny(乱搞做法)的更多相关文章
- BZOJ1278: 向量vector(计算几何 随机化乱搞)
题意 题目链接 Sol 讲一下我的乱搞做法.... 首先我们可以按极角排序.然后对\(y\)轴上方/下方的加起来分别求模长取个最大值.. 这样一次是\(O(n)\)的. 我们可以对所有向量每次随机化旋 ...
- BZOJ2744:[HEOI2012]朋友圈(最大团,乱搞)
Description 在很久很久以前,曾经有两个国家和睦相处,无忧无虑的生活着.一年一度的评比大会开始了,作为和平的两国,一个朋友圈数量最多的永远都是最值得他人的尊敬,所以现在就是需要你求朋友圈的最 ...
- [WC2018]通道(乱搞,迭代)
[洛谷题面]https://www.luogu.org/problemnew/show/P4221 这个题以及[CTSC2018 暴力写挂]都有类似的乱搞做法能通过考场数据. 具体搞法就是随一个起点, ...
- 洛谷 P3438 - [POI2006]ZAB-Frogs(乱搞/李超线段树)
题面传送门 首先一眼二分答案,我们假设距离 \((i,j)\) 最近的 scarefrog 离它的距离为 \(mn_{i,j}\),那么当我们二分到 \(mid\) 时我们显然只能经过 \(mn_{i ...
- Codeforces 306D - Polygon(随机化+乱搞)
Codeforces 题目传送门 & 洛谷题目传送门 中考终于结束了--简单写道题恢复下状态罢. 首先这一类题目肯定没法用一般的方法解决,因此考虑用一些奇淫的乱搞做法解决这道题,不难发现,如果 ...
- 【BZOJ-3578】GTY的人类基因组计划2 set + map + Hash 乱搞
3578: GTY的人类基因组计划2 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 367 Solved: 159[Submit][Status][ ...
- BZOJ_1800_[Ahoi2009]fly 飞行棋_乱搞
BZOJ_1800_[Ahoi2009]fly 飞行棋_乱搞 Description 给出圆周上的若干个点,已知点与点之间的弧长,其值均为正整数,并依圆周顺序排列. 请找出这些点中有没有可以围成矩形的 ...
- CF809E Surprise me!(莫比乌斯反演+Dp(乱搞?))
题目大意: 给你一棵树,树上的点编号为\(1-n\).选两个点\(i.j\),能得到的得分是\(\phi(a_i*a_j)*dis(i,j)\),其中\(dis(i,j)\)表示\(a\)到\(b\) ...
- hash进阶:使用字符串hash乱搞的姿势
前言 此文主要介绍hash的各种乱搞方法,hash入门请参照我之前这篇文章 不好意思hash真的可以为所欲为 在开头先放一下题表(其实就是我题解中的hash题目qwq) 查询子串hash值 必备的入门 ...
随机推荐
- X Open Cup named after E.V. Pankratiev. European Grand Prix
A. Arithmetic Rectangle 对于一行或者一列的情况可以递推求出最大值. 对于至少一行或者一列的情况,可以定义四个格子一组横向和纵向的相等关系,然后悬线法求最大子矩阵. 时间复杂度$ ...
- 根据文件大小自动判断单位B,KB,MB,GB
<php> /** * 文件大小格式化 * @param integer $size 初始文件大小,单位为byte * @return array 格式化后的文件大小和单位数组,单位为by ...
- python中栈的单链表实现
参考博客:https://www.cnblogs.com/stacklike/p/8284550.html 基于列表的简单实现 # 先进后出 # 以列表实现的简单栈 class SimpleStack ...
- 一般处理程序(ashx)的使用
ASP.NET 中发送请求的页面代码如下: <head runat="server"> <title></title> <script s ...
- HBuilder
什么是HBuilder? HBbuilder是DCloud(数字天堂)推出的一款支持HTML5的WEB开发IDE,主体是由java编写的,它将HTML/JS代码块进行代码封装,达到简单数据形成代码的特 ...
- 寻找真正的入口(OEP)--广义ESP定律
1.前言 在论坛上看到很多朋友,不知道什么是ESP定律,ESP的适用范围是什么,ESP定律的原理是什么,如何使用ESP定律?看到了我在“”调查结果发现,大家对ESP定律很感兴趣,当然因为实在是太好用了 ...
- IIC稳定性.VBS
Sub Main Dim cnt Dim delay Dim time Dim atttime atttime = 20 delay = 3000 time = 50 crt.screen.Send ...
- [Day16]常用API(正则表达式、Date类、DateFormat类、Calendar类)
1.正则表达式(Regular Expression,regex)-是一个字符串,使用单个字符串来描述.用来定义匹配规则,匹配一系列符合某个句法规则的字符串 1.1匹配规则: (1)字符:x -代表的 ...
- ItunesConnect:"Missing Push Notification Entitlement"警告-----以及解决方法
最近开发的cordova应用,要做ios的适配,并且发布版本,但是有一次在发测试版本的时候,突然收到一封邮件警告,原文如下: Missing Push Notification Entitlement ...
- TypeScript初探
TypeScript初探 TypeScript什么? 官方给的定义:TypeScript是一种由微软开发的自由和开源的编程语言,它是JavaScript类型的超集,可以编译成纯JavaScript,本 ...