[Nescafé 26] 小猫爬山&&售货员的难题&&区间众数(随机化大法好)

  两道正解都不是随机化的题被模拟退火 水过。。。

  小猫爬山:题目大意是有N只小猫,第i只小猫的体重为ai,有很多缆车,每辆缆车最多承重w,求需要最少多少辆缆车能承载所有的猫。。。

(1<=n<=18,1<=ai,w<=1e8)本来想背包,发现w太大,无奈之下随机化水过。

  模拟退火大法:先随机化小猫的排列,大约一千次,保存起来。对于每一种排列,再随机(大约50次),对于每一次随机出来的排列,从前到后贪心(这题直接贪心绝对错,但加上随机化正确率就高多了),如果贪心出的答案比之前的答案更小,则更新答案,同时更新之前的排列。。。然后就可以计算答案了,感觉正确率还是挺高的。

注:既然是模拟退火,那就还要控制温度,即:循环n次以上步骤,每次对排列进行的随机(就是打乱原序列)随着次数增加,随机的越来越不那么乱,这样就越来越接近正解的排列,这样贪心正确率就很高了。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<queue>
 5 #include<stack>
 6 #include<iostream>
 7 #include<ctime>
 8 using namespace std;
 9 int n,w,rpath[1000][20],ans[1000],a[20];
10 void adjust(int *p,int rn)
11 {
12     while(rn--)
13     {
14         int x=rand()%n+1;
15         int y=rand()%n+1;
16         swap(p[x],p[y]);
17     }
18 }
19 void get_rpath()
20 {
21     for(int i=0;i<1000;i++)
22     {
23         ans[i]=1e9;
24         for(int j=1;j<=n;j++)rpath[i][j]=j;
25         adjust(rpath[i],n-1);
26     }
27 }
28 int get_val(int *p)
29 {
30     int ret=0,sum=0;
31     for(int i=1;i<=n;i++)
32     {
33         sum+=a[p[i]];
34         if(sum>w){ret++;sum=a[p[i]];}
35     }
36     if(sum)ret++;
37     return ret;
38 }
39 int main()
40 {
41     freopen("koneko.in","r",stdin);freopen("koneko.out","w",stdout);
42     srand(time(NULL));
43     scanf("%d%d",&n,&w);
44     for(int i=1;i<=n;i++)scanf("%d",&a[i]);
45     get_rpath();
46     int t=n,p[20];
47     while(t--)
48     {
49         for(int i=0;i<1000;i++)
50         {
51             for(int j=0;j<50;j++)
52             {
53                 memcpy(p,rpath[i],sizeof(p));
54                 adjust(p,t);
55                 int tmp=get_val(p);
56                 if(tmp<ans[i])
57                 {
58                     ans[i]=tmp;
59                     memcpy(rpath[i],p,sizeof(rpath[i]));
60                 }
61             }
62         }
63     }
64     int res=ans[0];
65     for(int i=1;i<1000;i++)res=min(res,ans[i]);
66     cout<<res<<endl;
67     return 0;
68 }

 

  售货员的难题:经典TSP问题,有n座城市,告诉每两座城市的距离,售货员从1出发,要求不重复经过所有城市并回到1号城市,经过路径最小值。。。(n好像是20以内)

  模拟退火大法:仍然是随机化排列,基本步骤和上面完全相同,不同是贪心那一部分,事实上,根本不需要贪心,对每一种排列直接进行计算即可。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<queue>
 5 #include<stack>
 6 #include<iostream>
 7 #include<ctime> 
 8 using namespace std;
 9 int rpath[1000][50],n,mp[50][50],ans[1000];
10 void adjust(int *p,int rn)
11 {
12     while(rn--)
13     {
14         int x=rand()%(n-1)+1;
15         int y=rand()%(n-1)+1;
16         swap(p[x],p[y]);
17     }
18 }
19 void get_rpath()
20 {
21     for(int i=0;i<1000;i++)
22     {
23         ans[i]=1e9;
24         for(int j=1;j<n;j++)rpath[i][j]=j+1;
25         adjust(rpath[i],n-1);
26     }
27 }
28 int get_dis(int *p)
29 {
30     int q=1,ret=0;
31     for(int i=1;i<n;i++)
32     {
33         ret+=mp[q][p[i]];
34         q=p[i];
35     }
36     ret+=mp[q][1];
37     return ret;
38 }
39 int main()
40 {
41     freopen("salesman.in","r",stdin);freopen("salesman.out","w",stdout);
42     srand(time(NULL));
43     scanf("%d",&n);
44     for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)scanf("%d",&mp[i][j]);
45     get_rpath();
46     int t=n-1,p[50];
47     while(t--)
48     {
49         for(int i=0;i<1000;i++)
50         {
51             for(int j=0;j<50;j++)
52             {
53                 memcpy(p,rpath[i],sizeof(p));
54                 adjust(p,t);
55                 int tmp=get_dis(p);
56                 if(tmp<ans[i])
57                 {
58                     ans[i]=tmp;
59                     memcpy(rpath[i],p,sizeof(rpath[i]));
60                 }
61             }
62         }
63     }
64     int res=ans[0];
65     for(int i=1;i<1000;i++)res=min(res,ans[i]);
66     cout<<res<<endl;
67     return 0;
68 }

  其实感觉两份代码几乎一样,就只是贪心变成直接计算了而已。。。

  

  区间众数:题目大意是有n个数的序列,m个询问,每次询问l到r区间内是否有某一个数字出现次数超过区间一半。。。

  随机化大法:对每次询问,随机二十次区间里的任意一种颜色,可以log的时间内查找该颜色在区间内出现的次数,查找方法如下:每种颜色用一个vector保存出现的所有位置,查找时只需要用upper_bound(r)减去lower_bound(l)就可以得到次数。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<queue>
 5 #include<stack>
 6 #include<iostream>
 7 #include<ctime>
 8 using namespace std;
 9 vector<int>v[10005];
10 int n,a[300005],c;
11 int main()
12 {
13     freopen("color.in","r",stdin);freopen("color.out","w",stdout);
14     srand(time(NULL));
15     scanf("%d%d",&n,&c);
16     int l,r,q;
17     for(int i=1;i<=n;i++)
18     {
19         scanf("%d",&a[i]);
20         v[a[i]].push_back(i);
21     }
22     scanf("%d",&q);
23     for(int i=1;i<=q;i++)
24     {
25         scanf("%d%d",&l,&r);
26         bool ok=0;
27         for(int j=20;j;j--)
28         {
29             int k=l+rand()%(r-l+1);
30             int sum=upper_bound(v[a[k]].begin(),v[a[k]].end(),r)-lower_bound(v[a[k]].begin(),v[a[k]].end(),l);
31             if(sum>(r-l+1)/2){printf("yes %d\n",a[k]);ok=1;break;}
32         }
33         if(!ok)puts("no");
34     }
35     return 0;
36 }

 

  最后再说一句,虽然是三份ac过的代码,但并不能保证每次都过,第二份正确率大约在80%,第一份我是一遍就过了,所以不清楚,应该正确率更高,第三份错误率比1/2^20还要低,所以过不了的话可以去买彩票了。前两份过不了请多次提交

posted on 2016-10-23 21:17  deadpool66  阅读(158)  评论(0编辑  收藏  举报

导航