Fork me on GitHub

POJ3126-Prime Path

接着刷邝斌飞搜索专题

POJ3126 1s 65536K

洛谷 3s 0B

HDOJ1973  1s 32768K

啊啊啊看到好看小姑娘就无法专注,要好久才能进入状态,她也是计算机考研,眼睛睫毛好好看啊,身上好香好香,可我已经28了


昨天回家电脑又硬盘(3F0)需要放电,完事后A掉油田那个题目,今早来到图书馆Chrome自动关掉了,重启后密码库里的密码都没了,所有网站都要重新登陆,把用过的都登陆一遍后,设置的也没了,重新设置成恢复关闭页


她善良。像月亮姐姐,月亮姐姐像妈,一眼就看能出来,毫无抵抗力,她好真诚,曾经有人这样形容我,可是呢,但他中指戴着戒指。知道了自己想要什么样的人
View Code

这题本来想比如123想变成279,1/2/3进到第二层每个都有0-9这10种可能,这10种可能的每一个数进到第三层又有10种可能的数,那总共3个数,就是3*10*10次搜索。但想法不行啊,按照题意思如果123到279是这么搜的话任何3位数变3位数都3步,每一位变下就行了,但问题是可能有绕远去变成其他数的情况,因为要求质数。

 

这里先回顾质数,妈的看别人代码真是挣扎,看半天明白了,又去回顾do whlie开根号函数,优化里面的C代码

 1 #include <stdio.h>
 2 #include<math.h>
 3 int main()
 4 {
 5     double x, y, i;
 6     x = 3.0;
 7     do
 8     {
 9         i = 2.0;
10         do
11         {
12             y = x / i;
13             if(y != (int)y)//用于判断是否为整数
14             {
15                 if(i+1 > sqrt(x))
16                     printf("%d\n", (int)x);
17             }
18             i++;
19         }
20         while(i<sqrt(x)&&y!=(int)y);
21         x++;
22     }
23     while(x <= 100.0); //3到10000的素数
24 }
View Code

又去看回顾质数文章里的C++代码,就是一坨屎根本运行不了 ,根据思路优化

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<cmath>
 4 using namespace std;
 5 const int size=100;
 6 int zhishu[100];
 7 void work (){//主要程序
 8     zhishu[1]=2;
 9     int k=2;
10     for(int i=3;i<=size;i++){//枚举每个数
11         bool ok=1;
12         for(int j=1;j<k;j++){//枚举已经得到的质数
13             if(i%zhishu[j]==0){
14                 ok=!ok;
15                 break;
16             }
17         }
18         if(ok){
19             zhishu[k]=i;
20             cout<<"count"<<k<<' '<<i<<endl;
21             k++;
22         }
23     }
24 }
25 int main(){
26 //    freopen("zhishu.out","w",stdout);
27     cout<<"count1 2"<<endl;
28     work();
29 }
View Code

学到的知识点1: 算术基本定理:任一大于1的自然数,要么本身是质数,要么可以分解为几个质数之积,且这种分解是唯一的 判断数n是不是质数,可以把n从第一个质数2开始遍历已经有过的质数,如果能整除,则不是质数,如果遍历n之前所有质数,都无法整除,则n是质数。回顾质数涉及到的freopen文件打开

学到的知识点2:Pascal代码上面那段代码判断素数/质数用到的规律也很不错,强行记忆吧,挺巧妙的

 1 #include<stdio.h>
 2 #include<iostream>
 3 using namespace std;
 4 bool isPrime(int n)
 5 {
 6     if (n <= 3)
 7         return n > 1;
 8     else if (n % 2 == 0 || n % 3 == 0)
 9         return false;
10     else
11     {
12         for (int i=5; i*i<=n;i+=6){
13             if (n % i == 0 || n % (i + 2) == 0)
14                 return false;
15         }
16         return true;
17     }
18 }
19 int main()
20 {
21     int a;
22     while(scanf("%d", &a)!=EOF)
23         cout << isPrime(a)<<endl;
24 
25 //    for(int i=0;i<5;i++)
26 //        cout<<i<<endl;   //i++后还要再判断一下是否<5
27 }
View Code

 

时间复杂度之前研究过,又忘了,感觉挺麻烦,不管了,正好学质数/素数这块,顺便把经典的素数筛也回顾了

首先回顾啥叫logN复杂度,涉及logN复杂度啥叫它的底数真数对数的名词解释,看的第一篇文章感觉讲的很乱,第二篇文章,从暴力枚举的复杂度逐渐优化,有点理解了,但欧拉筛那,说2*3,3*2是重复的,可是埃筛里根本没这个啊,i=3的时候,J是从3开始的,第一个判断的数就是3*3,哪来的重复一说,我留言了,问题搁置,而且不懂为啥时间复杂度是O(n*log(logn)),问题搁置

回顾log以10为底叫lg,ln是以e为底,e为2.71828,其他底数输入方法(换底公式),得知,log以2为底3的对数,就lg3/lg2

第三篇文章,进一步讲解了埃筛和欧拉筛,其实想过从埃筛到欧拉素数筛直接加一个判断vis不行吗,有过的就别存了,但后来想通了,之前测试过,不管执不执行语句,只要for里有判断,就算是复杂度,也就是说,如果判断这个数之前有没有被前面的数用乘法搞成合数记录进来,这个判断的过程就已经占用时间复杂度了,至于判断完是略过还是存为合数都一样的,所以欧拉素数筛的重点是不要用判断抛除已经筛过的,而是直接在乘法找合数的时候就别循环到这个数,而且之前也测试过空跑1~10^13运行好久。但这文章也有问题,当i'等于i时,primes[j]就是primes[j+1]同一个质数啊,论证的什么玩意

第四篇文章还是优点没太懂,PS:玄学,新学的知识点如果没懂,哪怕不再去琢磨,随着时间的推移再回顾也会多理解一些

素数筛还挺牛逼的,想不通,暂时搁置

 

搜索比数论简单太多太多了,数论看的我眼睛花了都

 

艹她香水味好上头,磨炼习性,人家25考研才03年小姑娘
View Code

继续想这个题,跟深搜没关系,根据之前的深搜思路,比如1033深搜到顶应该是1039,但这都只是变换一次,相当于走一步。

或者从另一个角度想,深搜4个方向,先走上还是先走下无所谓,与顺序无关,回溯结束怎样都能走遍所有路径,但这个题比如给出1033,需要到2933,那深搜2033,再2933,还是先1933,再2933,这中途牵扯顺序,只能走质数

而且也没有最多不能超过几步,但会有Impossible的输出,想到广搜队列,1000~9999数是有限的,队列空了就Impossible。

 

那代码直接10min写出来,然后就RE超时了

暂时把素数筛照抄

  1 #include<stdio.h>
  2 #include<iostream>
  3 #include<queue>
  4 #include<string.h>
  5 using namespace std;
  6 int T;
  7 int a,b;
  8 struct Node{
  9     int num;
 10     int step;
 11 };
 12 queue<Node>q;
 13 
 14 
 15 bool isprime[10000]; // isprime[i]表示i是不是素数
 16 int prime[10000]; // 现在已经筛出的素数列表
 17 int n=10000; // 上限,即筛出<=n的素数
 18 int cnt; // 已经筛出的素数个数
 19 void euler()
 20 {
 21     memset(isprime, true, sizeof(isprime)); // 先全部标记为素数
 22     isprime[1] = false; // 1不是素数
 23     for(int i = 2; i <= n; ++i) // i从2循环到n(外层循环)
 24     {
 25         if(isprime[i]) prime[++cnt] = i;
 26         // 如果i没有被前面的数筛掉,则i是素数
 27         for(int j = 1; j <= cnt && i * prime[j] <= n; ++j)
 28         // 筛掉i的素数倍,即i的prime[j]倍
 29         // j循环枚举现在已经筛出的素数(内层循环)
 30         {
 31             isprime[i * prime[j]] = false;
 32             // 倍数标记为合数,也就是i用prime[j]把i * prime[j]筛掉了
 33             if(i % prime[j] == 0) break;
 34             // 最神奇的一句话,如果i整除prime[j],退出循环
 35             // 这样可以保证线性的时间复杂度
 36         }
 37     }
 38 }
 39 
 40 
 41 int main()
 42 {
 43 
 44     cin>>T;
 45     while(T--){
 46         int flag=0;
 47         cin>>a>>b;
 48 
 49         Node node;
 50         node.num=a;
 51         node.step=0;
 52         q.push(node);
 53         euler();
 54 
 55         while(!q.empty()){
 56             Node thisnode=q.front();
 57             if(thisnode.num==b){
 58                 flag=1;
 59                 cout<<thisnode.step<<endl;
 60                 while(!q.empty())
 61                     q.pop();
 62                 break;
 63             }
 64             Node thisnodecopy=thisnode;
 65             q.pop();
 66             //当作每次转换也没有前导0
 67 
 68             //首位每次减1/加1
 69             for(int i=1; thisnode.num-1000*i>=1000 ;i++){
 70                 thisnodecopy.num=thisnode.num-1000*i;
 71                 thisnodecopy.step=thisnode.step+1;
 72                 if(isprime[thisnodecopy.num]==true)
 73                     q.push(thisnodecopy);
 74             }
 75             for(int i=1; thisnode.num+1000*i<=9999 ;i++){
 76                 thisnodecopy.num=thisnode.num+1000*i;
 77                 thisnodecopy.step=thisnode.step+1;
 78                 if(isprime[thisnodecopy.num]==true)
 79                     q.push(thisnodecopy);
 80             }
 81 
 82             //第二位每次减1/加1
 83             for(int i=1; (thisnode.num-100*i)/1000==thisnode.num/1000 ;i++){
 84                 thisnodecopy.num=thisnode.num-100*i;
 85                 thisnodecopy.step=thisnode.step+1;
 86                 if(isprime[thisnodecopy.num]==true)
 87                     q.push(thisnodecopy);
 88             }
 89             for(int i=1; (thisnode.num+100*i)/1000==thisnode.num/1000; i++){
 90                 thisnodecopy.num=thisnode.num+100*i;
 91                 thisnodecopy.step=thisnode.step+1;
 92                 if(isprime[thisnodecopy.num]==true)
 93                     q.push(thisnodecopy);
 94             }
 95 
 96             //第三位每次减1/加1
 97             for(int i=1; (thisnode.num-10*i)/100==thisnode.num/100; i++){
 98                 thisnodecopy.num=thisnode.num-10*i;
 99                 thisnodecopy.step=thisnode.step+1;
100                 if(isprime[thisnodecopy.num]==true)
101                     q.push(thisnodecopy);
102             }
103             for(int i=1; (thisnode.num+10*i)/100==thisnode.num/100; i++){
104                 thisnodecopy.num=thisnode.num+10*i;
105                 thisnodecopy.step=thisnode.step+1;
106                 if(isprime[thisnodecopy.num]==true)
107                     q.push(thisnodecopy);
108             }
109 
110             //末位每次减1/加1
111             for(int i=1; (thisnode.num-1*i)/10==thisnode.num/10; i++){
112                 thisnodecopy.num=thisnode.num-1*i;
113                 thisnodecopy.step=thisnode.step+1;
114                 if(isprime[thisnodecopy.num]==true)
115                     q.push(thisnodecopy);
116             }
117             for(int i=1; (thisnode.num+1*i)/10==thisnode.num/10; i++){
118                 thisnodecopy.num=thisnode.num+1*i;
119                 thisnodecopy.step=thisnode.step+1;
120                 if(isprime[thisnodecopy.num]==true)
121                     q.push(thisnodecopy);
122             }
123         }
124         if(flag==0)
125             cout<<"Impossible"<<endl;
126     }
127 }

 

真他妈绝了,把53行的 euler(); 放到main函数里变成第一句话,这回居然洛谷是最弱数据了,居然AC了,HDOJ是MLE,POJ是TLE

1.02s 0B?? 结合三个平台的限制,感觉平台的测评机制应该有差异,怎么可能0B在hdojMLE

 

看了这个博客,发现自己居然没给BFS设置vis数组

修改后三个平台均AC,傻逼POJ又崩了,第二天好了也AC了

 AC代码

  1 #include<stdio.h>
  2 #include<iostream>
  3 #include<queue>
  4 #include<string.h>
  5 using namespace std;
  6 int T;
  7 int a,b;
  8 struct Node{
  9     int num;
 10     int step;
 11 };
 12 int vis[10000];
 13 queue<Node>q;
 14 
 15 
 16 bool isprime[10000]; // isprime[i]表示i是不是素数
 17 int prime[10000]; // 现在已经筛出的素数列表
 18 int n=10000; // 上限,即筛出<=n的素数
 19 int cnt; // 已经筛出的素数个数
 20 void euler()
 21 {
 22     memset(isprime, true, sizeof(isprime)); // 先全部标记为素数
 23     isprime[1] = false; // 1不是素数
 24     for(int i = 2; i <= n; ++i) // i从2循环到n(外层循环)
 25     {
 26         if(isprime[i]) prime[++cnt] = i;
 27         // 如果i没有被前面的数筛掉,则i是素数
 28         for(int j = 1; j <= cnt && i * prime[j] <= n; ++j)
 29         // 筛掉i的素数倍,即i的prime[j]倍
 30         // j循环枚举现在已经筛出的素数(内层循环)
 31         {
 32             isprime[i * prime[j]] = false;
 33             // 倍数标记为合数,也就是i用prime[j]把i * prime[j]筛掉了
 34             if(i % prime[j] == 0) break;
 35             // 最神奇的一句话,如果i整除prime[j],退出循环
 36             // 这样可以保证线性的时间复杂度
 37         }
 38     }
 39 }
 40 
 41 
 42 int main()
 43 {
 44 
 45     euler();
 46     cin>>T;
 47     while(T--){
 48         memset(vis, 0, sizeof(vis));
 49         int flag=0;
 50         cin>>a>>b;
 51 
 52         Node node;
 53         node.num=a;
 54         node.step=0;
 55         q.push(node);
 56 
 57         while(!q.empty()){
 58             Node thisnode=q.front();
 59             if(thisnode.num==b){
 60                 flag=1;
 61                 cout<<thisnode.step<<endl;
 62                 while(!q.empty())
 63                     q.pop();
 64                 break;
 65             }
 66             Node thisnodecopy=thisnode;
 67             q.pop();
 68             //当作每次转换也没有前导0
 69 
 70             //首位每次减1/加1
 71             for(int i=1; thisnode.num-1000*i>=1000 ;i++){
 72                 thisnodecopy.num=thisnode.num-1000*i;
 73                 thisnodecopy.step=thisnode.step+1;
 74 
 75                 if(isprime[thisnodecopy.num]==true&&vis[thisnodecopy.num]==0){
 76                     vis[thisnodecopy.num]=1;
 77                     q.push(thisnodecopy);
 78                 }
 79             }
 80             for(int i=1; thisnode.num+1000*i<=9999 ;i++){
 81                 thisnodecopy.num=thisnode.num+1000*i;
 82                 thisnodecopy.step=thisnode.step+1;
 83                 if(isprime[thisnodecopy.num]==true&&vis[thisnodecopy.num]==0){
 84                     vis[thisnodecopy.num]=1;
 85                     q.push(thisnodecopy);
 86                 }
 87             }
 88 
 89             //第二位每次减1/加1
 90             for(int i=1; (thisnode.num-100*i)/1000==thisnode.num/1000 ;i++){
 91                 thisnodecopy.num=thisnode.num-100*i;
 92                 thisnodecopy.step=thisnode.step+1;
 93                 if(isprime[thisnodecopy.num]==true&&vis[thisnodecopy.num]==0){
 94                     vis[thisnodecopy.num]=1;
 95                     q.push(thisnodecopy);
 96                 }
 97             }
 98             for(int i=1; (thisnode.num+100*i)/1000==thisnode.num/1000; i++){
 99                 thisnodecopy.num=thisnode.num+100*i;
100                 thisnodecopy.step=thisnode.step+1;
101                 if(isprime[thisnodecopy.num]==true&&vis[thisnodecopy.num]==0){
102                     vis[thisnodecopy.num]=1;
103                     q.push(thisnodecopy);
104                 }
105             }
106 
107             //第三位每次减1/加1
108             for(int i=1; (thisnode.num-10*i)/100==thisnode.num/100; i++){
109                 thisnodecopy.num=thisnode.num-10*i;
110                 thisnodecopy.step=thisnode.step+1;
111                 if(isprime[thisnodecopy.num]==true&&vis[thisnodecopy.num]==0){
112                     vis[thisnodecopy.num]=1;
113                     q.push(thisnodecopy);
114                 }
115             }
116             for(int i=1; (thisnode.num+10*i)/100==thisnode.num/100; i++){
117                 thisnodecopy.num=thisnode.num+10*i;
118                 thisnodecopy.step=thisnode.step+1;
119                 if(isprime[thisnodecopy.num]==true&&vis[thisnodecopy.num]==0){
120                     vis[thisnodecopy.num]=1;
121                     q.push(thisnodecopy);
122                 }
123             }
124 
125             //末位每次减1/加1
126             for(int i=1; (thisnode.num-1*i)/10==thisnode.num/10; i++){
127                 thisnodecopy.num=thisnode.num-1*i;
128                 thisnodecopy.step=thisnode.step+1;
129                 if(isprime[thisnodecopy.num]==true&&vis[thisnodecopy.num]==0){
130                     vis[thisnodecopy.num]=1;
131                     q.push(thisnodecopy);
132                 }
133             }
134             for(int i=1; (thisnode.num+1*i)/10==thisnode.num/10; i++){
135                 thisnodecopy.num=thisnode.num+1*i;
136                 thisnodecopy.step=thisnode.step+1;
137                 if(isprime[thisnodecopy.num]==true&&vis[thisnodecopy.num]==0){
138                     vis[thisnodecopy.num]=1;
139                     q.push(thisnodecopy);
140                 }
141             }
142         }
143         if(flag==0)
144             cout<<"Impossible"<<endl;
145     }
146 }

###:如果涉及while输入问题,scanf 不写!=EOF遇到ctrlZ就不会退出,cin是流,没法用比较符,但遇到ctrlZ自动退出

###:阅读洛谷讨论区,了解其他写法,挺简洁的

古树旋律和未来人生需要更新,手机1/128G了,赛车也不想玩了,看的泰剧爱的复仇也没劲noon老了,管子也导撸腻了
View Code

于是,昨晚回家想素数筛这个东西,比如用20个素数来说明

2357111317192329313741434753596167

有时候代码看不懂也别强求,Q神都不看别人代码,好奇HIT吴师兄和北邮导师们怎么看别人代码和纸上判题的, 其实代码看不懂第二天再看就感觉懂了不少,但别强求,每个人思路不一样哪怕AC掉的题,再看自己写的代码,涉及各种变量和加加减减也很难看懂,重要的是思路
View Code

用到的是上面第四篇文章超链接,图可以看第二篇文章的超链接(但4和5都有问题,我在这里重新解释下)

埃筛是比如5,找合数会遍历从5*5到5*200,比如范围是1000,那到5*125是625的时候会筛一次,而对于25这数,25起手就乘25也会把625遍历一次,根据上面说的这个也算复杂度就多余了

欧拉素数筛先是1设置为false它不是素数,比如找从2到100内的素数

2加入素数表且是表里第一个,2乘第一个素数2,由此4设置为了false,2%2==0,这一层就结束了

再接着循环来到了3,3没被筛过,加入素数表作为第二个素数,3乘已有的素数2,6设置为false,3乘已有的素数3,9设置为false,3%3==0,这一层结束了

循环来到了4,4是false,也就是合数不进表,4乘第一个素数2,8设置为false,此时4%2是0,结束循环了

再接着循环来到了5,5没被筛过,加入素数表作为第三个素数,5乘已有的素数2,10设置为false,5乘已有的素数3,15设置为false,5乘已有的素数刚加进来的自己5,25设置为false,5%5==0,结束循环

再接着循环来到了6,6是false过,也就是合数不进表,6乘第一个素数2,12设置为false,6%2==0,结束循环

再接着循环来到了7,7没被筛过,加入素数表作为第四个素数,7乘第一个素数2,3,5,7,14,21,35,49设置为false,7%7==0,结束循环

再接着循环来到了8,8是false过,也就是合数不进表,8乘第一个素数2,16设置为false,16%2==0,结束循环

.....

如此类推,可见用到的数

循环    素表           筛掉合数

2     2              2*表里的2

3     2 3                     3*表里的2 、3*表里的3

4     2 3           4*表里的2、4*表里的3

5     2 3 5     5*表里的2、5*表里的3、5*表里的5

6     2 3 5     6*表里的2、6*表里的3、6*表里的5

7     2 3 5 7    7*表里的2、7*表里的3、7*表里的5、7*表里的7

8       2 3 5 7          8*表里的2、8*表里的3、8*表里的5、8*表里的7

9       2 3 5 7               9*表里的2、9*表里的3、9*表里的5、9*表里的7

发现埃筛是从2.3.4.5.....开始每次筛它们的倍数,然后优化是发现比如5,5不用从1开始乘去找倍数,因为2的时候一定乘过5,3的时候也一定乘过5,4的时候也一定乘过5,那交换律,5已经乘过了2,3,4,只需要需要从5开始乘就可以了

但欧拉筛你反过来,我只找比我小的数去乘

三点需要论证的,时间复杂度,我自己都不会,先搁置

目前需要考虑的就两点,一是有没有遗漏的,二有没有重复的

随便拿出一个数5举例子,5乘的数2,3,5,2,3,5一定都没乘过比它们大的5,所以不重复

至于6乘的数只有2,这个不管,只要没重复就行,下面考虑有没有遗漏的

4到2不乘了,6到2不乘了,8到2不乘了,9到3不乘了

有点高中数学求和然后都约掉那个感觉,也有点像我那个刷的第一道题奶牛的草稿图里的剪枝

意思就是

4*表里的3,可以变成2*2*3,下面6*2会筛

6*表里的3,可以变成2*3*3,下面的9*2会筛,6*表里的5,可以变成2*3*5,下面的15*2会筛

8*表里的3,可以变成2*4*3,下面的12*2会筛,8*表里的5,可以变成2*4*5,下面的20*2会筛,8*表里的7,可以变成2*4*7,下面的28*2会筛,

9*表里的3,可以变成3*3*3,下面的9*3会筛,9*表里的5,可以变成3*3*5,下面的15*3会筛,9*表里的7,可以变成3*3*7,下面的21*3会筛,

 

发现自己读几篇文章有个大概思路方向,自己思考比较好,实在受不了那些傻逼名词,什么质因数什么最大最小这那的,很影响理解

回忆睛神PAT,回忆柳神男女PAT满分收徒,手把手辅导HIT/BUPT考生机试上岸。如今垃圾外包测试
View Code

 

 

至此,此题完结

posted @ 2024-09-20 01:56  GerJCS  阅读(7)  评论(0编辑  收藏  举报