POJ3126-Prime Path
接着刷邝斌飞搜索专题
POJ3126 1s 65536K
洛谷 3s 0B
HDOJ1973 1s 32768K
啊啊啊看到好看小姑娘就无法专注,要好久才能进入状态,她也是计算机考研,眼睛睫毛好好看啊,身上好香好香,可我已经28了
昨天回家电脑又硬盘(3F0)需要放电,完事后A掉油田那个题目,今早来到图书馆Chrome自动关掉了,重启后密码库里的密码都没了,所有网站都要重新登陆,把用过的都登陆一遍后,设置的也没了,重新设置成恢复关闭页
她善良。像月亮姐姐,月亮姐姐像妈,一眼就看能出来,毫无抵抗力,她好真诚,曾经有人这样形容我,可是呢,但他中指戴着戒指。知道了自己想要什么样的人
这题本来想比如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 }
又去看回顾质数文章里的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 }
学到的知识点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 }
时间复杂度之前研究过,又忘了,感觉挺麻烦,不管了,正好学质数/素数这块,顺便把经典的素数筛也回顾了
首先回顾啥叫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年小姑娘
继续想这个题,跟深搜没关系,根据之前的深搜思路,比如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老了,管子也导撸腻了
于是,昨晚回家想素数筛这个东西,比如用20个素数来说明
2、3、5、7、11、13、17、19、23、29、31、37、41、43、47、53、59、61、67
有时候代码看不懂也别强求,Q神都不看别人代码,好奇HIT吴师兄和北邮导师们怎么看别人代码和纸上判题的, 其实代码看不懂第二天再看就感觉懂了不少,但别强求,每个人思路不一样哪怕AC掉的题,再看自己写的代码,涉及各种变量和加加减减也很难看懂,重要的是思路
用到的是上面第四篇文章超链接,图可以看第二篇文章的超链接(但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考生机试上岸。如今垃圾外包测试
至此,此题完结