帮助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了。如果书的高度种类有很多的话,这种方法可以比正解更好用,难道不是吗?

本片文章纯属乱搞,神犇不要喷……

最新文章

  1. [NOI2006] 最大获利
  2. js转换数据类型为浮点型,并取两位小数点
  3. Unquotted string &#39;"2016-07-19"&#39;
  4. iOS开发——高级技术&amp;系统应用于系统服务
  5. spring读写分离(配置多数据源)[marked]
  6. JS幻灯片,循环播放,滚动导航,jQuery平滑旋转幻灯片
  7. nagios 实现Mysql 主从同步状态的监控
  8. LNMP、LAMP、LANMP一键安装脚本(定期更新)[转]
  9. WEB Application Development Integrator : 应用设置
  10. ES(二): Build ES Cluster on Azure VM
  11. JavaScript获取浏览器版本等信息
  12. [ExtJS5学习笔记]第三十一节 sencha extjs 5使用cmd生成的工程部署到tomcat服务器
  13. 【一天一道LeetCode】#350. Intersection of Two Arrays II
  14. JAVA 数组作为方法参数—传递地址
  15. Win2008服务启动不能调用Office Word的解决方法
  16. C#拷贝一个库的表到另外一个库中(的四种方式)
  17. 自动化 数据分离 --A文件里面的类 中的函数 调用 B文件里面类 的函数 的方法
  18. Python入门之面向对象module,library,package之间区别
  19. Vundle,Vim 的 Bundle(转)
  20. Java基础——类加载机制

热门文章

  1. [jzoj]2538.【NOIP2009TG】Hankson 的趣味题
  2. jquery复制图片
  3. html页面转成jsp页面之后样式变化的问题解决方法
  4. 一个简单的分布式session框架
  5. 电子产品使用感受之——为什么我把Apple Watch S2 升级到了 S4?
  6. 为Vue.js添加友好日志
  7. vue中router.go、router.push和router.replace的区别
  8. 海思编译链编译出现__aeabi_unwind_cpp_pr1重定义怎么回事
  9. Vue2.2版本学习小结
  10. jmeter 之 beanshell sample