帮助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值 必备的入门 ...
随机推荐
- URL 链接中 井号#、问号?、连接符& 分别有什么作用?
在一个 URL 中可以包含很多的内容,其中不仅仅是包含 26 个英文字母,10 个罗马数字,中文汉字,还可以拥有井号“#”.问号“?”.连接符“&”等三种最常见的符号,那么这些符号在网站中都有 ...
- ie下js不执行的几种可能
1.首先考虑的就是代码是否有报错2.其次搜索代码中是否有console.log等测试的代码3.检查所用的方法是否兼容ie 以上是鄙人在开发过程中遇到的几种情况,抛砖来引玉~~
- Golang 微框架 Gin 简介
框架一直是敏捷开发中的利器,能让开发者很快的上手并做出应用,甚至有的时候,脱离了框架,一些开发者都不会写程序了.成长总不会一蹴而就,从写出程序获取成就感,再到精通框架,快速构造应用,当这些方面都得心应 ...
- javaScript---RegExp
字符串是编程时涉及到的最多的一种数据结构,对字符串进行操作的需求几乎无处不在.比如判断一个字符串是否是合法的Email地址,虽然可以编程提取@前后的子串,再分别判断是否是单词和域名,但这样做不但麻烦, ...
- codecademy课程笔记——JavaScript Promise
Promise是一种表示异步操作最终的结果的对象,一个Promise对象有三种状态 Pending: 初始状态 ,操作还未完成 Fullfilled:操作成功完成,且这个promise现在有一个r ...
- 九、JSP入门(1)
JSP入门 1 JSP概述 1.1 什么是JSP JSP(Java Server Pages)是JavaWeb服务器端的动态资源.它与html页面的作用是相同的,显示数据和获取数据. 1.2 JSP的 ...
- java实现文章敏感词过滤检测
SensitivewordFilter.java import java.util.HashSet; import java.util.Iterator; import java.util.Map; ...
- Spring-day02
Annotation复习:1,Annotation:作为类型的元数据; 1,给类型加标记; 2,annotation可以添加各种类型的属性;2,Annotation的上的标记: 1),target:标 ...
- Linux_相关命令(学习,备忘)
1.Linux 查看实时cpu使用率: top 说明:top命令即时显示process的动态 2.查看cpu处理器使用率: cat /proc/stat 3.平均cpu使用率 4.赋予文件夹下所有文件 ...
- ASP.NET Core 集成测试中结合 WebApplicationFactory 使用 SQLite 内存数据库
SQLite 内存数据库(in-memory database)的连接字符串是 Data Source=:memory: ,它的特点是数据库连接一关闭,数据库就会被删除.而使用 services. ...