帮助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(乱搞做法)的更多相关文章

  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. python文本读写数据

    # 写方法1 f = open('tmp.txt','w') f.write('hello world') f.close() # 写方法2 with open('tmp.txt','w') as f ...

  2. Java三大集合框架

    定义:java中集合类:是一种工具类,就像是容器,储存任意数量的具有共同属性的对象 一.List集合 1.List实现的超级父类接口:Collection 2.了解ArrayList类 A):定义的格 ...

  3. __x__(3)0905第二天__W3C标准集合

    World Wide Web Consortium 万维网联盟(外语缩写:W3C)标准不是某一个标准,而是一系列标准的集合. 创建于 1994 年,是 Web 技术领域最具权威和影响力的国际中立性技术 ...

  4. [LeetCode] Chalkboard XOR Game 黑板亦或游戏

    We are given non-negative integers nums[i] which are written on a chalkboard.  Alice and Bob take tu ...

  5. amoeba实现读写分离

    amoeba的运行环境依靠java的jdk: 下面执行amoeba的安装不走 # mkdir /usr/local/src/amoeba 上传文件:amoeba-mysql-binary-2.2.0. ...

  6. C语言面试题分类->宏定义

    1.写一个“标准”宏,这个宏输入两个参数并返回较小的一个 答:#define MIN(x, y) ((x)<(y)?(x):(y))//注意x,y要加括号,因为x,y如果有复合运算会出现问题. ...

  7. Digest of Overview of Linux Kernel Security Features

    Linux kernel Security: I. DAC: Discretionary Access Control, the core security model of UNIX. II. PO ...

  8. Express全系列教程之(九):将session上传至mysql数据库

    一.简介 实际引用中,有些公司在不同地区会设置不同服务器,因此就需要用到nginx以实现负载均衡,这时,将session数据保存至数据库就成为了需要面对的问题,我们以MySQL数据库为例,看看他是如何 ...

  9. 微信小程序轮播图组件 swiper,swiper-item及轮播图片自适应

    官网地址:https://developers.weixin.qq.com/miniprogram/dev/component/swiper.html index.wxml文件 indicator-d ...

  10. pkg-config 详解

    转载自:https://blog.csdn.net/newchenxf/article/details/51750239 1 什么是pkg-config pkg-config是一个linux下的命令, ...