帮助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. mysql 查询 两个表中不同字段的 和,并通过两个表的时间来分组

    ( SELECT sum( a.cost_sum ) AS sum_cost, sum( a.phone_sum ) AS sum_phone, sum( a.arrive_sum ) AS sum_ ...

  2. JavaScript继承的几种模式

    原型链 让一个类的原型对象指向另一个类的实例

  3. python全栈开发 * 表格标签 表单标签 css 引入方式 * 180807

    html部分 一.表格标签<table> 1.一个表格<table>由每行<tr>组成的,每行是由<td>组成的. 注意: 一个表格是由行组成的(行是由 ...

  4. c语言,以单词为单位逆序字符串

    #include "string.h" #include "stdio.h" char * nixu(char *c) { ; int n = strlen(c ...

  5. 如何查询注册表的值及 Powershell 应用

    利用 c:\windows\system32\reg.exe 的 query 参数即可. reg.exe 的参数如下: C:\windows\system32> reg.exe /?REG Op ...

  6. 【Python基础】lpthw - Exercise 39 字典

    1. 字典和列表的区别 对于列表,它是一些项的有序排列,只能通过数值来进行索引:对于字典,则可以通过许多东西进行索引,它采用键-值映射的方式,不存在一个特定的顺序,因此不能用类似列表的数值索引,但它的 ...

  7. [Day23]IO(字节流、字符流)

    1.字节流 1.1 字节输出流OutputStream-抽象类,表示输出字节流的所有类的超类 (1)void close() 关闭此输出流并释放与此流相关的所有系统资源 (2)void flush() ...

  8. 2018-2019-2 网络对抗技术 20165317 Exp2 后门原理与实践

    2018-2019-2 网络对抗技术 20165317 Exp2 后门原理与实践 基础问题回答 例举你能想到的一个后门进入到你系统中的可能方式? 下载免费应用的时候会有绑定木马. 浏览某些网页时会有内 ...

  9. LeetCode 217 Contains Duplicate 解题报告

    题目要求 Given an array of integers, find if the array contains any duplicates. Your function should ret ...

  10. 2018年年度总结 & 2019年计划

      2018关键词 「探索」 引用以前作文最爱写的开头,时间如白驹过隙,回想上次写17年年度总结,仿佛也就过了几日光景.   首先回顾一下17年定下的目标, 18年我将关键字设为探索,目的有两个,一是 ...