第二题:bfs,忘了将queue清空。
第三题:bfs,记得使用vis数组,防止重复入队
第三题:bfs会MLE。DFS只需使用到long long的19位长度即可。
分析:
BFS显然会按照2^N增长;还没找到答案之前内存就爆炸了。
至于为什么可以用long long解决,不说前人探索出来的规律。你想想,如果是暴力搜索DFS,如果答案真得可能会到100位,那就会超出时间限制,所以位数不会太长。
但要是超过了19位呢?那就用结构体来模拟除法咯,不过还是要用DFS。
第四题:
处在简单搜索这个题目集里面真是刺激到我了,所以人有点浮躁。然后还打游戏,精神不好,出了很多错误。所以,认真对待,用最好的面貌去迎接挑战。
#include#include #include #define maxn 25 #define sc scanf #define pt printf #define rep(i,a,b) for(int i=a;iint a[maxn][maxn],op[maxn][maxn],out[maxn][maxn]; int m,n,cnt,ans; --op的核心是如果上面的位置是黑色,那么他就应该是为1. --但是,很简单的话语,包含了这个意思: 你可以省去了对a源数组的临时复制,只需要考虑周边产生的操作以及原位置的数值即可得到当前的op值 int main() { #ifdef WANGNINGLOCAL freopen("in.txt","r",stdin); #endif --这个是没有必要的,因为全局数组声明的时候未初始化,自动填充0. --而且,根本不会用到出界的a数组。 --所以是逼急了的情况下抓的无用的稻草。 memset(a,0,sizeof(a)); while(~sc("%d%d",&n,&m)) { ans=0x3f3f3f3f; rep(i,1,n+1) rep(j,1,m+1) sc("%d",&a[i][j]); rep(b,0,1<<m) { //pt("当前用于分解的二进制对象是 %d\n",b); memset(op,0,sizeof(op)); cnt=0; for(int i=m;i>=1;--i) { --考虑到字典序最小,所以将从右往左赋最低到最高位。 op[1][i] = (b>>(m-i))&1; //pt("在填第%d位,内容是%d\n",i,op[1][i]); cnt += op[1][i]; } rep(i,2,n+1) { rep(j,1,m+1) { int jg = a[i-1][j] + op[i-1][j] + op[i-1][j-1] + op[i-1][j+1]; if(i>=3) jg+=op[i-2][j]; if(jg%2==1) { op[i][j]=1; --这里是一个大错误,之前把上面的代码复制下来,那实际上完全就不应该用op[1][i]来在这里累加cnt cnt += op[i][j]; } } } int i; for(i=1;i<=m;++i) { int jg = a[n][i] + op[n][i] + op[n][i-1] + op[n][i+1] +op[n-1][i]; if(jg%2==1) break; } if(i==m+1) { if(cnt<ans) { ans=cnt; memcpy(out,op,sizeof(op)); } } } if(ans==0x3f3f3f3f) pt("IMPOSSIBLE\n"); else { --这里都是错误,因为之前没有写else语句。 rep(i,1,n+1) rep(j,1,m+1) pt("%d%c",out[i][j]," \n"[j==m]); } } return 0; }
回过头来修改的自己最初的代码版本,里面居然纠正出了五个错误
#include#include #include #include
第五题: 使用了BFS,思路是先找出1000~9999之间的质数,然后对每个这样的质数寻找变动一位后的所有数,用map
之后对每个输入的起点数,只要用结构体
typedef struct nod{
int what,dis;
}nod;
what代表到了哪个数,dis表示走了几步,使用BFS就行了。
注意点:
1.仍旧是queue的清空问题——虽然这回我是没有在上面犯错的,但依旧觉得很重要。
2.vis数组使用了,时间在0.3秒; 没用使用,时间在2.4秒。 —— 仅从样例来看。
所以,走过的路,已经有人走过了,就不用再走,不论前方是成功还是失败,成功了果实不是你的,因为你的dis比别人的大,也就是来得晚了;
别人如果失败了,那你干嘛要走呢?另寻他途吧。
#include#include #include #include
第六题:数组大小开错了,要注意合并的长度会达到2*C。
#include#include #include<set> #include #include using namespace std; #define sc scanf #define pt printf #define maxn 105 int main() { set<string> s; #ifdef WANGNINGLOCAL freopen("in.txt","r",stdin); #endif int T,len,i,run=0,cnt; string ob; char a[maxn],b[maxn],tar[2*maxn],cp[2*maxn]; sc("%d",&T); while(T--) { for(i=0;i '\0'; s.clear(); cnt=0; sc("%d",&len); sc("%s%s%s",a,b,tar); while(1) { ++cnt; for(i=0;i i) { cp[i*2] = b[i]; cp[i*2+1] = a[i]; } if(strcmp(cp,tar)==0) break; ob=cp; if(s.find(ob)!=s.end()) { cnt=-1; break; } else { s.insert(ob); for(i=0;i i) { a[i] = cp[i] ; b[i] = cp[i+len] ; } } } pt("%d %d\n",++run,cnt); } return 0; }
第七题:总结和思路放在代码里
#include#include #include<set> #include #include using namespace std; #define sc scanf #define pt printf #define maxn 105 /* 思路: 使用BFS,对六种操作依次压入。然后取出后执行{ 将当前操作压入操作路径; 根据操作类型修改值; 判断修改后的值之前是否得到过: 未得到过——在此基础上压入六种操作, 否则——不处理 ; 判断修改后的值是否有等于C的值,有则退出; } 注意点: POUR的解读:要么把j倒满,要么把i倒完,但是不会有浪费 使用了set来代替vis,实际上由于A B C都在1~100之间,所以可以用二维的vis处理。 第一次测试样例没过,因为在AB之间倒水的环节,先执行了清零才进行将水倒给另外一个罐子的操作。 第一发没过,因为没有处理impossible的情况。 */ typedef struct st{ int a,b; bool operator < (const st &x)const { if(a==x.a) return b<x.b; else return a<x.a; } }st; typedef struct ob{ char now; string way; st g; }ob; ob op; queue q; set s; int A,B,C,diff,i,len,flag=0; void myadd() { op.now='0'; q.push(op);//"111" op.now='1'; q.push(op);//"122" op.now='2'; q.push(op);//"211" op.now='3'; q.push(op);//"222" op.now='4'; q.push(op);//"312" op.now='5'; q.push(op);//"321" } int main() { #ifdef WANGNINGLOCAL freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); #endif sc("%d%d%d",&A,&B,&C); op.way=""; op.g.a=0; op.g.b=0; myadd(); while(!q.empty()) { op=q.front(); q.pop(); op.way+=op.now; switch(op.now){ case '0':{ op.g.a=A; };break; case '1':{ op.g.b=B; };break; case '2':{ op.g.a=0; };break; case '3':{ op.g.b=0; };break; case '4':{ diff = B - op.g.b; if(op.g.a >= diff) { op.g.b = B; op.g.a -= diff; } else{ op.g.b += op.g.a; op.g.a = 0; } };break; case '5':{ diff = A - op.g.a; if(op.g.b >= diff) { op.g.b -= diff; op.g.a = A; } else{ //这里出过错,因为b清零了不能再把原来预定的水资源给a op.g.a += op.g.b; op.g.b = 0 ; } };break; } if(s.find(op.g)==s.end()){ s.insert(op.g); myadd(); } if(op.g.a==C||op.g.b==C) { flag=1; break; } } if(flag==0) { pt("impossible\n"); return 0; } len = op.way.length(); pt("%d\n",len); for(i=0;i i) { switch(op.way[i]){ case '0':pt("FILL(1)\n");break; case '1':pt("FILL(2)\n");break; case '2':pt("DROP(1)\n");break; case '3':pt("DROP(2)\n");break; case '4':pt("POUR(1,2)\n");break; case '5':pt("POUR(2,1)\n");break; } } return 0; }
第八题:错误回顾和关键点放在代码里。
思路就是BFS,选定两个点,然后往四周扩散,如果烧过了就不管,否则就将其压入queue,并且其状态置为烧过了。
#include#include #include<set> #include #include using namespace std; #define sc scanf #define pt printf #define maxn 15 #define inf 0x3f3f3f3f int T,N,M,cnt,tot,ans,out; char a[maxn][maxn],b[maxn][maxn]; int dix[4]={1,-1,0,0}; int diy[4]={0,0,1,-1}; typedef struct ob{ int x,y,dis; }ob; ob op,tp; queue q; int test_set(ob z) { if(z.x<0||z.x>=N||z.y<0||z.y>=M||b[z.x][z.y]!='#') return 0; else { b[z.x][z.y]='.'; q.push(z); ans=max(ans,z.dis); return 1; } } 错误回顾 1.字符串数组开成一维的了 2.z.y>=M 写成了z.x>=M 3.中间为了节省时间,我使用了:{ 如果上一轮选的点都不是干草堆,那么下一次就不用再让a复制给b。 }的操作,但是问题在于,{ 对应的变量是mytri,mytri会在cnt大于0的时候置1,cnt大于0代表选的点有干草堆。 mytri的作用是,如果是1,那么代表需要将a复制给b; 如果是0,那么代表之前最初选取的两个点都不是干草堆,所以就没有对b做出修改,所以就可以沿用此次的复制值(来自整个a),所以这样可以节省复制的时间。 但是最大的问题是:一开始你一定要将它设置为1,因为b总要有第一次的复制过程吧。 那你一开始是要给他赋初值1的。 这样想是对的,关键是,设置为1的位置放错了。 你想想,b第一次要获取a的值,是在a有值了之后,而每次a什么时候会更新呢? 每个test case都会有新的进来啊。 所以说,如果我们在上一个case的最后阶段,没有成功的选取到非干草堆的情况,那么mytri会是0, 而mytri=1被我放在了while(T--)前面了,所以会出错。 就那样例来说,我这样的处理方式最后是同时选取了最后一个点,而最后一个点是'.'——非干草堆,所以出现了下一轮b没有被a初始化的错误。 } 关键点: ans是每次搜索之后,当前的最远的距离(火烧到这个grid经历的grid数) 由于每次搜索是N*M中的两个点,所以每次选定两个点之后都要重新置零。 out是搜索成功的最短路径值,所以一开始要设置成inf那么大 int main() { #ifdef WANGNINGLOCAL freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); #endif int i,j,k,mytri=1,run=0,dec; sc("%d",&T); while(T--) { mytri=1; dec = 0; out = inf; sc("%d%d",&N,&M); getchar(); tot=0; memset(a,'\0',sizeof(a)); memset(b,'\0',sizeof(b)); for(i=0;i i){ sc("%s",a[i]); for(j=0;j if(a[i][j]=='#') ++tot; } for(i=0;i i) { for(j=i;j j) { cnt=ans=0; while(!q.empty()) q.pop(); if(mytri==1) memcpy(b,a,sizeof(a)); op.dis=0; op.x=i/M; op.y=i%M; cnt += test_set(op); op.x=j/M; op.y=j%M; cnt += test_set(op); if(cnt>0) //有可以纵火的点 { mytri=1; while(!q.empty()) { op=q.front(); q.pop(); tp.dis = op.dis + 1; for(k=0;k<4;++k) { tp.x = op.x + dix[k]; tp.y = op.y + diy[k]; cnt += test_set(tp); } if(cnt==tot) { dec = 1; out = min( out, ans ) ; break; } } } else { mytri = 0; } } } pt("Case %d: %d\n",++run,dec==1?out:-1); } return 0; }
第九题:BFS,要特别注意更新火场情况的部分。
思路和注意事项都放在代码里面,最后出错的部分我用--标记了,而不是//。
所以你复制了我的代码之后,直接编译,就能看到是在哪一行出错的。
……当然能AC了吧……UVA崩掉了的说,我交一发看看。
……TLE。让我改改。2019年5月4日15:52:19
嗯,改出来了,结果是Joe走迷宫的时候,没有处理已经走过的情况,最终导致了TLE。所以我在Joe走迷宫的时候,对走过的点设置成了'J'。
看别人的代码,虽然很多是细枝末节的,但是也学到了许多。
首先,判断的规则最好简单明了,参考程序在判断的时候用了很多if-continue; 你感觉多,你感觉人家笨,你感觉你可以用一句话概括……但是之后查错的时候,简洁明了的if-continue真的可以很方便理解——查错的时候脑子往往很乱——今天很乱,乱了好几天了……——所以一定要简洁方便阅读。
而且判断有没有走过,其实可以直接用vis来处理。
下面贴出的代码,是能ac的,我知道确定起始点后把那个点的位置上的字符改成'.'没有必要的,fire_test函数有返回值也是没有必要的。
但是就贴在那里吧,我不改了。
最有意思的想法是我会根据时间来动态更新火场情况。
#include#include #include<set> #include #include using namespace std; #define sc scanf #define pt printf #define maxn 1005 #define inf 0x3f3f3f3f /*思路蛮简单的,维护两个queue, 一个关于火场的,这个要配合 { 将输入的maze作相应的修改 } 操作 一个是关于joe的,对他当前可以去的square进行入队,当他已经在边界之外了,那么就可以结束了, 这个要配合{ 出界的情况反而是可以终止 while(!q.empty()) 的break条件 观察四周可用的square 变量flag,来预防特殊情况:如果当qj空了,还没有逃出火场+迷宫,那么就输出IMPOSSIBLE }*/ char a[maxn][maxn]; int dix[4] = {1, -1, 0, 0}; int diy[4] = {0, 0, 1, -1}; int R, C; typedef struct ob { int x, y, dis; } ob; ob op, tp, fire, adj; queue q; //获取Joe在dis时刻所在的位置 queue f; //获取火焰在dis时刻所在的位置 int test_set(ob z) { if (a[z.x][z.y] =='.' && (z.x <= 0 || z.x >= R-1 || z.y <= 0 || z.y >= C-1) ) return 2; //表明走出边界了 else if (a[z.x][z.y] == '#' || a[z.x][z.y] == 'F'|| a[z.x][z.y] == 'J') return 0; else { a[z.x][z.y] = 'J' ; q.push(z); return 1; } } int fire_test_set(ob z) { if (z.x < 0 || z.x >= R || z.y < 0 || z.y >= C || a[z.x][z.y] == 'F' || a[z.x][z.y] == '#') return 0; else { f.push(z); a[z.x][z.y] = 'F' ; return 1; } } /*哇,我居然在提前写总结 本意是为了不走重复的路,直接将所在位置置为墙壁,但是这样是不行的, 因为火有可能蔓延到你那个位置。 所以要用vis数组——不如直接在a数组的基础上修改,变成火了就标成'F'。 ……这好像就是应该这样的。 而且为了判断的方便——没必要判断是'J'还是'.','J'本质上就是'.',只不过Joe刚好从那里出发而已。 所以应该将J所在位置探明后,就将其变成'.'。 火的dis也是有必要记录的,你不能一次性让f pop+push把整个迷宫都点燃吧?*/ int main() { #ifdef WANGNINGLOCAL freopen("in.txt", "r", stdin); //freopen("out.txt","w",stdout); #endif int T, pre; //pre用来存储dis——如果现在出来的dis和之前的不一样了,那么代表已经进入了下一秒 sc("%d", &T); while (T--) { while (!q.empty()) q.pop(); while (!f.empty()) f.pop(); sc("%d%d", &R, &C); int i, j, flag = 0, explore=1, state; op.dis = 1; for (i = 0; i < R; ++i) { sc("%s", a[i]); //如果没有找到起始位置,那么就特地判断一遍有没有包含J。 for (j = 0; j < C; ++j) { if (a[i][j] == 'F') { op.x = i; op.y = j; f.push(op); } else if (explore && a[i][j] == 'J') { op.x = i; op.y = j; q.push(op); explore = 0; a[i][j] = '.' ; } } } op = q.front(); q.pop(); if( test_set(op)==2 ) { pt("%d\n", 1); continue; } //pre是用来更新火场情况的变量,它的值是dis时刻值。 //如果我们发现当前的dis是新的时刻了,那么就会将新的时刻赋值给pre //然后更新新的火场情况 pre = 1; while (!q.empty()) { op = q.front(); q.pop(); tp.dis = op.dis + 1; if (pre != tp.dis) { pre = tp.dis; //更新火场情况的语句块 while (!f.empty()) { fire = f.front(); //我们是希望得到这一秒的火场情况 //所以我们要的是上一秒的火场情况,即火焰的fire.dis时刻值<当前时刻 //--之前的错误就是写成了 if (fire.dis > pre ) break; if (fire.dis >= pre ) break; //如果是上一秒的,那么就pop f.pop(); adj.dis = fire.dis + 1; for (i = 0; i < 4; ++i) { adj.x = fire.x + dix[i]; adj.y = fire.y + diy[i]; fire_test_set(adj); } } } //冷静观察火场,寻找逃生之路 for (i = 0; i < 4; ++i) { tp.x = op.x + dix[i]; tp.y = op.y + diy[i]; state = test_set(tp) ; if (state == 2) //能逃出火场 { flag = 1; break; } } if (flag) break; } if (flag) pt("%d\n", tp.dis); else pt("IMPOSSIBLE\n"); } return 0; }
第十题:难度突然降下来了。但是犯的错是——没有将符合条件的对象push进queue。
开始想到了vis,但是后来没有写;
输出结果的时候,没有按照最初的想法,i是要小于输出序列的一半的
——注意力,专心点
#include#include #include<set> #include #include using namespace std; #define sc scanf #define pt printf #define maxn 15 #define inf 0x3f3f3f3f int dir[][2]={{0,1},{0,-1},{1,0},{-1,0}}; int vis[maxn][maxn],a[maxn][maxn]; typedef struct ob{ string s; int x,y; }ob; ob op,tp; int main() { #ifdef WANGNINGLOCAL freopen("in.txt", "r", stdin); //freopen("out.txt","w",stdout); #endif int i,j,k; memset(vis,0,sizeof(vis)); for(i=0;i<5;++i) for(j=0;j<5;++j) sc("%d",&a[i][j]); op.x=0; op.y=0; op.s="00"; vis[0][0] = 1; queue q; q.push(op); while(!q.empty()) { op = q.front(); q.pop(); if(op.x==4 && op.y == 4) break; tp.s = op.s; for(i=0;i<4;++i) { j = tp.x = op.x + dir[i][0]; k = tp.y = op.y + dir[i][1]; if(j<0||j>4||k<0||k>4) continue; if(a[j][k] == 1) continue; if(vis[j][k]==1) continue; tp.s += (char)( '0' + j ); tp.s += (char)( '0' + k ); vis[j][k] = 1; q.push(tp); } } for(i=0;i<(int)op.s.length()/2;++i) { pt("(%c, %c)\n",op.s[i<<1],op.s[i<<1|1]); } return 0; }
第十一题:嗯,难度很低。就是加上了四个方向。
BFS=dir[][2] + vis[][] + a[][] + queue + 清晰的规则理解 + 正确的细节处理
怎么走 走过没 里面有什么内容 BFS本身需要的数据结构 人应该有的素质
#include#include #include<set> #include #include using namespace std; #define sc scanf #define pt printf #define maxn 105 #define inf 0x3f3f3f3f int dir[][2]={{0,1},{0,-1},{1,0},{-1,0},{1,1},{1,-1},{-1,-1},{-1,1}}; int vis[maxn][maxn]; char a[maxn][maxn]; typedef struct ob{ int x,y; }ob; int R,C; ob op,tp; queue q; int main() { #ifdef WANGNINGLOCAL freopen("in.txt", "r", stdin); //freopen("out.txt","w",stdout); #endif while(sc("%d%d",&R,&C)&&R) { int i,j,k,tx,ty,cnt; for(i=0;i "%s",a[i]); memset(vis,0,sizeof(vis)); while(!q.empty()) q.pop(); cnt=0; for(i=0;i i) { for(j=0;j j) { if(!vis[i][j]&&a[i][j]=='@') { ++cnt; vis[i][j] = 1; op.x = i; op.y = j; q.push(op); while(!q.empty()) { op = q.front(); q.pop(); for(k=0;k<8;++k) { tx = tp.x = op.x + dir[k][0]; ty = tp.y = op.y + dir[k][1]; if(tx<0||tx>=R||ty<0||ty>=C) continue; if(a[tx][ty] == '*') continue; if(vis[tx][ty]==1) continue; vis[tx][ty] = 1; q.push(tp); } } } } } pt("%d\n",cnt); } return 0; }
第十二题:1.没有赋初值的变量不要为了图快而使用‘+=’
2.业务规则理解。
#include#include #include<set> #include #include using namespace std; #define sc scanf #define pt printf #define maxn 105 #define inf 0x3f3f3f3f int dir[][2]={{0,1},{1,0},{0,2},{2,0},{1,2},{2,1}}; int vis[maxn][maxn][maxn]; typedef struct ob{ int x[3],dis; }ob; int N,M,S; ob op,tp; queue q; int main() { #ifdef WANGNINGLOCAL freopen("in.txt", "r", stdin); //freopen("out.txt","w",stdout); #endif while(sc("%d%d%d",&S,&N,&M)&&N&&M&&S) { if(S%2==1) { pt("NO\n"); continue; } int i,j,ans,giv,rec,diff,cei,res,cnt; memset(vis,0,sizeof(vis)); while(!q.empty()) q.pop(); ans = -1; op.x[0] = S; op.x[1] = op.x[2] = 0; op.dis=0; vis[S][0][0] = 1; q.push(op); while(!q.empty()) { op = q.front(); q.pop(); //for(int ii=0;ii<3;++ii) pt("op %d%c",op.x[ii]," \n"[ii==2]); cnt = 0; for(i=0;i<3;++i) if(op.x[i]==S/2) ++cnt; if(cnt==2) { ans = op.dis; break; } tp.dis = op.dis +1 ; for(i=0;i<6;++i) { giv = dir[i][0]; rec = dir[i][1]; for(j=0;j<3;++j) { if(j==rec||j==giv) continue; res = j; } tp.x[res] = op.x[res] ; if(rec==0) cei = S; else if(rec==1) cei=N; else cei = M; diff = cei - op.x[rec]; if(op.x[giv] >= diff){ tp.x[rec] = cei; tp.x[giv] = op.x[giv] - diff; } else{ tp.x[rec] = op.x[rec] + op.x[giv]; tp.x[giv] = 0; } if( vis[tp.x[0]][tp.x[1]][tp.x[2]]==0 ) { vis[tp.x[0]][tp.x[1]][tp.x[2]]=1; //pt(" %d to %d : cei = %d, diff = %d ",giv,rec,cei,diff); //for(int ii=0;ii<3;++ii) pt(" %d%c",tp.x[ii]," \n"[ii==2]); q.push(tp); } } } if(ans!=-1) { pt("%d\n",ans); //for(int ii=0;ii<3;++ii) pt("%d%c",op.x[ii]," \n"[ii==2]); } else pt("NO\n"); } return 0; }