帮助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分(还可以了)

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cmath>
 4 #include<algorithm>
 5 #include<cstdlib>
 6 #include<cstring>
 7 using namespace std;
 8 typedef long long ll;
 9 ll read(){
10     ll ans=0;
11     char ch=getchar(),last=' ';
12     while(ch>'9'||ch<'0'){
13         last=ch;
14         ch=getchar();
15     }
16     while(ch<='9'&&ch>='0'){
17         ans=ans*10+ch-'0';
18         ch=getchar();
19     }
20     if(last=='-')
21         ans=-ans;
22     return ans;
23 }
24 int n,m,a[200],b[100],d[100],x,y,ans,sum,last,pre,tot;
25 bool c[105];
26 double T;
27 const double ze=1e-8;
28 int random(int x){
29     unsigned int ans=x;
30     ans*=1234567891;
31     return ans;
32 }
33 int main(){
34     //freopen("bb.in","r",stdin);
35     n=read();
36     m=read();
37     for(int i=1;i<=n;i++)
38         a[i]=read(),b[a[i]]++;
39     T=10000;
40     ans=n;
41     for(int i=1;i<=m;i++)
42         c[i]=true,b[a[i]]--,d[a[i]]++;
43     if(n==m){
44         ans=0;
45         for(int i=25;i<=32;i++)
46             if(d[i])
47                 ans++;
48         printf("%d\n",ans);
49         return 0;
50     }
51     while(T>ze){
52         //tot++;
53         x=rand()%n+1;
54         while(c[x])
55             x=rand()%n+1;
56         y=rand()%n+1;
57         while(!c[y])
58             y=rand()%n+1;
59         c[x]=true;
60         c[y]=false;
61         b[a[x]]--;
62         b[a[y]]++;
63         d[a[x]]++;
64         d[a[y]]--;
65         sum=0;
66         last=1;
67         while(c[last])
68             last++;
69         if(last<=n){
70             sum=1;
71             pre=last;
72             for(int i=last+1;i<=n;i++)
73                 if(!c[i]&&a[pre]!=a[i]){
74                     sum++;
75                     pre=i;
76                 }
77         }
78         for(int i=25;i<=32;i++)
79             if(d[i]&&!b[i])
80                 sum++;
81         if(ans>sum)
82             ans=sum;
83         else{
84             if(rand()%25+1>log(T+20)){
85                 c[x]=false;
86                 c[y]=true;
87                 b[a[x]]++;
88                 b[a[y]]--;
89                 d[a[x]]--;
90                 d[a[y]]++;
91             }
92         }
93         T*=0.99998;
94     }
95     printf("%d\n",ans);
96     //printf("%d %d\n",ans,tot);
97     return 0;
98 }

 

赛后百度了一下正宗的模拟退火法,把代码修改一下,是这样的(90分,差不多了)

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cmath>
 4 #include<algorithm>
 5 #include<cstdlib>
 6 #include<cstring>
 7 using namespace std;
 8 typedef long long ll;
 9 ll read(){
10     ll ans=0;
11     char ch=getchar(),last=' ';
12     while(ch>'9'||ch<'0'){
13         last=ch;
14         ch=getchar();
15     }
16     while(ch<='9'&&ch>='0'){
17         ans=ans*10+ch-'0';
18         ch=getchar();
19     }
20     if(last=='-')
21         ans=-ans;
22     return ans;
23 }
24 int n,m,a[200],b[100],d[100],x,y,ans,sum,last,pre,tot;
25 bool c[105];
26 double T,de;
27 const double ze=1e-8;
28 int main(){
29     //freopen("bb.in","r",stdin);
30     n=read();
31     m=read();
32     for(int i=1;i<=n;i++)
33         a[i]=read(),b[a[i]]++;
34     T=100000;
35     ans=n;
36     for(int i=1;i<=m;i++)
37         c[i]=true,b[a[i]]--,d[a[i]]++;
38     if(n==m){
39         ans=0;
40         for(int i=25;i<=32;i++)
41             if(d[i])
42                 ans++;
43         printf("%d\n",ans);
44         return 0;
45     }
46     if(m==0){
47         printf("%d\n",n);
48         return 0;
49     }
50     while(T>ze){
51         //tot++;
52         x=rand()%n+1;
53         while(c[x])
54             x=rand()%n+1;
55         y=rand()%n+1;
56         while(!c[y])
57             y=rand()%n+1;
58         c[x]=true;
59         c[y]=false;
60         b[a[x]]--;
61         b[a[y]]++;
62         d[a[x]]++;
63         d[a[y]]--;
64         sum=0;
65         last=1;
66         while(c[last])
67             last++;
68         if(last<=n){
69             sum=1;
70             pre=last;
71             for(int i=last+1;i<=n;i++)
72                 if(!c[i]&&a[pre]!=a[i]){
73                     sum++;
74                     pre=i;
75                 }
76         }
77         for(int i=25;i<=32;i++)
78             if(d[i]&&!b[i])
79                 sum++;
80         if(ans>sum)
81             ans=sum;
82         else{
83             de=sum-ans;
84             if((1.0/exp(de/T))*100<=rand()%100+1){
85                 c[x]=false;
86                 c[y]=true;
87                 b[a[x]]++;
88                 b[a[y]]--;
89                 d[a[x]]--;
90                 d[a[y]]++;
91             }
92         }
93         T*=0.99998;
94     }
95     printf("%d\n",ans);
96     //printf("%d %d\n",ans,tot);
97     return 0;
98 }

 

可惜这样得不了ac,然后ctime不能用,srand()也没办法,怎么办?手动改随机种子
这个95分

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cmath>
  4 #include<algorithm>
  5 #include<cstdlib>
  6 #include<cstring>
  7 using namespace std;
  8 typedef long long ll;
  9 ll read(){
 10     ll ans=0;
 11     char ch=getchar(),last=' ';
 12     while(ch>'9'||ch<'0'){
 13         last=ch;
 14         ch=getchar();
 15     }
 16     while(ch<='9'&&ch>='0'){
 17         ans=ans*10+ch-'0';
 18         ch=getchar();
 19     }
 20     if(last=='-')
 21         ans=-ans;
 22     return ans;
 23 }
 24 int n,m,a[200],b[100],d[100],x,y,ans,sum,last,pre,tot;
 25 bool c[105];
 26 double T,de;
 27 const double ze=1e-8;
 28 int main(){
 29     //freopen("bb.in","r",stdin);
 30     for(int i=1;i<=160;i++)
 31         n=rand();
 32     n=read();
 33     m=read();
 34     for(int i=1;i<=n;i++)
 35         a[i]=read(),b[a[i]]++;
 36     T=1000000;
 37     ans=n;
 38     for(int i=1;i<=m;i++)
 39         c[i]=true,b[a[i]]--,d[a[i]]++;
 40     if(n==m){
 41         ans=0;
 42         for(int i=25;i<=32;i++)
 43             if(d[i])
 44                 ans++;
 45         printf("%d\n",ans);
 46         return 0;
 47     }
 48     if(m==0){
 49         printf("%d\n",n);
 50         return 0;
 51     }
 52     while(T>ze){
 53         //tot++;
 54         x=rand()%n+1;
 55         while(c[x])
 56             x=rand()%n+1;
 57         y=rand()%n+1;
 58         while(!c[y])
 59             y=rand()%n+1;
 60         c[x]=true;
 61         c[y]=false;
 62         b[a[x]]--;
 63         b[a[y]]++;
 64         d[a[x]]++;
 65         d[a[y]]--;
 66         sum=0;
 67         last=1;
 68         while(c[last])
 69             last++;
 70         if(last<=n){
 71             sum=1;
 72             pre=last;
 73             for(int i=last+1;i<=n;i++)
 74                 if(!c[i]&&a[pre]!=a[i]){
 75                     sum++;
 76                     pre=i;
 77                 }
 78         }
 79         for(int i=25;i<=32;i++)
 80             if(d[i]&&!b[i])
 81                 sum++;
 82         if(ans>sum)
 83             ans=sum;
 84         else{
 85             de=sum-ans;
 86             if((1.0/exp(de/T))*100<=rand()%100+1){
 87                 c[x]=false;
 88                 c[y]=true;
 89                 b[a[x]]++;
 90                 b[a[y]]--;
 91                 d[a[x]]--;
 92                 d[a[y]]++;
 93             }
 94         }
 95         T*=0.99998;
 96     }
 97     printf("%d\n",ans);
 98     //printf("%d %d\n",ans,tot);
 99     //printf("%.6lf\n",exp(2));
100     return 0;
101 }

 

难道到极限了吗?当然没有,一定可以ac的,我调了半天一直WA,同学看了一下,只改了一个字符,就ac了(强)。
下面是ac的代码

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cmath>
  4 #include<algorithm>
  5 #include<cstdlib>
  6 #include<cstring>
  7 using namespace std;
  8 typedef long long ll;
  9 ll read(){
 10     ll ans=0;
 11     char ch=getchar(),last=' ';
 12     while(ch>'9'||ch<'0'){
 13         last=ch;
 14         ch=getchar();
 15     }
 16     while(ch<='9'&&ch>='0'){
 17         ans=ans*10+ch-'0';
 18         ch=getchar();
 19     }
 20     if(last=='-')
 21         ans=-ans;
 22     return ans;
 23 }
 24 int n,m,a[200],b[100],d[100],x,y,ans,sum,last,pre,tot;
 25 bool c[105];
 26 double T,de;
 27 const double ze=1e-8;
 28 int main(){
 29     //freopen("bb.in","r",stdin);
 30     for(int i=1;i<=160;i++)
 31         n=rand();
 32     n=read();
 33     m=read();
 34     for(int i=1;i<=n;i++)
 35         a[i]=read(),b[a[i]]++;
 36     T=2000000;
 37     ans=n;
 38     for(int i=1;i<=m;i++)
 39         c[i]=true,b[a[i]]--,d[a[i]]++;
 40     if(n==m){
 41         ans=0;
 42         for(int i=25;i<=32;i++)
 43             if(d[i])
 44                 ans++;
 45         printf("%d\n",ans);
 46         return 0;
 47     }
 48     if(m==0){
 49         printf("%d\n",n);
 50         return 0;
 51     }
 52     while(T>ze){
 53         tot++;
 54         x=rand()%n+1;
 55         while(c[x])
 56             x=rand()%n+1;
 57         y=rand()%n+1;
 58         while(!c[y])
 59             y=rand()%n+1;
 60         c[x]=true;
 61         c[y]=false;
 62         b[a[x]]--;
 63         b[a[y]]++;
 64         d[a[x]]++;
 65         d[a[y]]--;
 66         sum=0;
 67         last=1;
 68         while(c[last])
 69             last++;
 70         if(last<=n){
 71             sum=1;
 72             pre=last;
 73             for(int i=last+1;i<=n;i++)
 74                 if(!c[i]&&a[pre]!=a[i]){
 75                     sum++;
 76                     pre=i;
 77                 }
 78         }
 79         for(int i=25;i<=32;i++)
 80             if(d[i]&&!b[i])
 81                 sum++;
 82         if(ans>sum)
 83             ans=sum;
 84         else{
 85             de=sum-ans;
 86             if((1.0/exp(de/T))*100<=(double)(rand()%100+1)){
 87                 c[x]=false;
 88                 c[y]=true;
 89                 b[a[x]]++;
 90                 b[a[y]]--;
 91                 d[a[x]]--;
 92                 d[a[y]]++;
 93             }
 94         }
 95         T*=0.999978;
 96     }
 97     printf("%d\n",ans);
 98     //printf("%d %d\n",ans,tot);
 99     return 0;
100 }

 

调随机种子是很傻的做法(比赛时不可能完成),调参数才更有效。
同样AC,不要调随机种子,在退火概率中,还有一个系数是可以改的。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cmath>
 4 #include<algorithm>
 5 #include<cstdlib>
 6 #include<cstring>
 7 using namespace std;
 8 typedef long long ll;
 9 ll read(){
10     ll ans=0;
11     char ch=getchar(),last=' ';
12     while(ch>'9'||ch<'0'){
13         last=ch;
14         ch=getchar();
15     }
16     while(ch<='9'&&ch>='0'){
17         ans=ans*10+ch-'0';
18         ch=getchar();
19     }
20     if(last=='-')
21         ans=-ans;
22     return ans;
23 }
24 int n,m,a[200],b[100],d[100],x,y,ans,sum,last,pre,tot;
25 bool c[105];
26 double T,de;
27 const double ze=1e-8;
28 int main(){
29     n=read();
30     m=read();
31     for(int i=1;i<=n;i++)
32         a[i]=read(),b[a[i]]++;
33     T=2000000;
34     ans=n;
35     for(int i=1;i<=m;i++)
36         c[i]=true,b[a[i]]--,d[a[i]]++;
37     if(n==m){
38         ans=0;
39         for(int i=25;i<=32;i++)
40             if(d[i])
41                 ans++;
42         printf("%d\n",ans);
43         return 0;
44     }
45     if(m==0){
46         printf("%d\n",n);
47         return 0;
48     }
49     while(T>ze){
50         tot++;
51         x=rand()%n+1;
52         while(c[x])
53             x=rand()%n+1;
54         y=rand()%n+1;
55         while(!c[y])
56             y=rand()%n+1;
57         c[x]=true;
58         c[y]=false;
59         b[a[x]]--;
60         b[a[y]]++;
61         d[a[x]]++;
62         d[a[y]]--;
63         sum=0;
64         last=1;
65         while(c[last])
66             last++;
67         if(last<=n){
68             sum=1;
69             pre=last;
70             for(int i=last+1;i<=n;i++)
71                 if(!c[i]&&a[pre]!=a[i]){
72                     sum++;
73                     pre=i;
74                 }
75         }
76         for(int i=25;i<=32;i++)
77             if(d[i]&&!b[i])
78                 sum++;
79         if(ans>sum)
80             ans=sum;
81         else{
82             de=sum-ans;
83             if((1.0/(exp(de/(T*1.2))))*100<=(double)(rand()%100+1)){
84                 c[x]=false;
85                 c[y]=true;
86                 b[a[x]]++;
87                 b[a[y]]--;
88                 d[a[x]]--;
89                 d[a[y]]++;
90             }
91         }
92         T*=0.999978;
93     }
94     printf("%d\n",ans);
95     //printf("%d %d\n",ans,tot);
96     return 0;
97 }

 

于是,这种费正解也把这道题ac了。如果书的高度种类有很多的话,这种方法可以比正解更好用,难道不是吗?

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

转载于:https://www.cnblogs.com/RetardedZY/p/7469880.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值