帮助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值 必备的入门 ...
随机推荐
- ajax多图上传
百度云代码 参考:https://segmentfault.com/q/1010000004218827
- 20181115 python-第一章学习小结part1
知识点回顾: 什么是编程: 写代码,让计算机执行任务 编程语言的分类与特性: 1.机器语言,即二进制语言,最帖近于机器底层,可以由计算机直接执行,故速度最快,但不适合开发. 2.汇编语言,直接将二进制 ...
- EasyUI 分页 偶遇 问题
当 存在大量 重复 数据字段的 时候 entity.AsNoTracking().ToList().Skip((page.pageNumber - 1) * page.rows).Take(page. ...
- 2018-2019-1 20189210 《LInux内核原理与分析》第九周作业
进程的切换和系统的一般执行过程 (1)进程调度的时机 1.schedule是一个内核函数,不是一个系统调用,进程的调度只发生在内核中,进程调度函数schedule()只能在内核中被调用,用户进程无法调 ...
- amazeui表单form异步提交方法
// 留言提交 $(function() { var $form = $('#ue-form'); $form.validator({ validateOnSubmit: true, submit: ...
- js判断设备是否为安卓
var u = navigator.userAgent; var isAndroid = u.indexOf('Android') > -1 || u.indexOf('Adr') > - ...
- Python Learning - Three
1. Set Set is a collection which is unordered and unindexed. No duplicate members In Python sets ar ...
- Idea使用说明
快捷键 ctrl + alt + b : 查找接口的实现类 ctrl + h : 查找类或接口的继承关系 double shift : 搜索任何地方 ctrl + shift + n : 查找文件 c ...
- ListView添加图片文字项
1)listview 控件 结合 imagelist 控件 实现类似效果. 2)添加 imagelist 控件 images 属性,点击后面的... 添加相应图片. 3)点listview,查看其属性 ...
- Python 3+selenium+unittest+HTMLTestRunner生成测试报告
一.下载HTMLTestRunner.py,解压,将它放到 python安装路径的site-packages目录下 https://pan.baidu.com/s/1epWlibxbxWlNoIcxL ...