ACM必做50题的解题搜索.docx
- 文档编号:30668617
- 上传时间:2023-08-19
- 格式:DOCX
- 页数:31
- 大小:24.42KB
ACM必做50题的解题搜索.docx
《ACM必做50题的解题搜索.docx》由会员分享,可在线阅读,更多相关《ACM必做50题的解题搜索.docx(31页珍藏版)》请在冰豆网上搜索。
ACM必做50题的解题搜索
POJ1011Sticks搜索+强剪枝(终于AC了,分享经验)
这个题目是不是贪心的,我就是第一次用了贪心,一直WA,相当的悲剧,贪心错误的sample:
715118884321,所以大家还是全部搜索。
但是全部搜索必须剪枝,不然肯定是TLE的,而且本体属于强剪枝,少剪了也是TLE。
经典搜索题,果然是到处充斥着剪枝才能过啊,我的代码离剪到极限还差很多
题目给出一大堆小棍子的长度,需要把他们拼成几根长度相等的大棍子,求大棍子的最短长度
看自己剪枝方法的效果时候,可以添设一个变量来记录递归次数
如剪枝4:
没有这个剪枝的情况下对以下数据需要40万次递归,而加上这个剪枝后减少到了4万多次
对数据:
45
1532411188815324111888153241118881532411188815324111888
#include
#include
usingnamespacestd;
intsticks[65];
intused[65];
intn,len;
booldfs(inti,intl,intt)//i为当前试取的棍子序号,l为要拼成一根完整的棍子还需要的长度,t初值为所有棍子总长度
{
if(l==0)
{
t-=len;
if(t==0)returntrue;
for(i=0;used[i];++i);//剪枝1:
搜索下一根大棍子的时候,找到第一个还没有使用的小棍子开始
used[i]=1;//由于排序过,找到的第一根肯定最长,也肯定要使用,所以从下一根开始搜索
if(dfs(i+1,len-sticks[i],t))returntrue;
used[i]=0;
t+=len;
}
else
{
for(intj=i;j { if(j>0&&(sticks[j]==sticks[j-1]&&! used[j-1]))//剪枝2: 前后两根长度相等时,如果前面那根没被使用,也就是由前面那根 continue;//开始搜索不到正确结果,那么再从这根开始也肯定搜索不出正确结果,此剪枝威力较大 if(! used[j]&&l>=sticks[j])//剪枝3: 最简单的剪枝,要拼成一根大棍子还需要的长度L>=当前小棍子长度,才能选用 { l-=sticks[j]; used[j]=1; if(dfs(j,l,t))returntrue; l+=sticks[j]; used[j]=0; if(sticks[j]==l)//剪枝4: 威力巨大的剪枝,程序要运行到此处说明往下的搜索失败,若本次的小棍长度刚好填满剩下长度,但是后 break;//面的搜索失败,则应该返回上一层 } } } returnfalse; } boolcmp(constinta,constintb) { returna>b; } intmain() { while(cin>>n&&n) { intsum=0; for(inti=0;i { cin>>sticks[i]; sum+=sticks[i]; used[i]=0; } sort(sticks,sticks+n,cmp);//剪枝5: 从大到小排序后可大大减少递归次数 boolflag=false; for(len=sticks[0];len<=sum/2;++len)//剪枝6: 大棍长度一定是所有小棍长度之和的因数,且最小因数应该不小于小棍中最长的长度 { if(sum%len==0) { if(dfs(0,len,sum)) { flag=true; cout< break; } } } if(! flag) cout< } return0; } 本文来自CSDN博客,转载请标明出处: poj1033Defragment 题意: 磁盘整理,按照从第一个文件到最后一个文件的顺序排放,而且每个文件的碎片按原来的顺序放在一起,要求转移的次数最少。 解: 其实根本不用搜索,一开始想搜索想了很久,上网找解题报告也没找到(这么水的一题竟然没有解题报告),于是开始自已想。 其实碎片的排列只有二种情况: 1.A0碎片没有放在原来的位置,而它原来的位置正好是空的。 而A1碎片也刚好没有放在原来的位置,而b原来的位置之前一直被A0占领,同样还有A2碎片没有在原来位置,而其原来的位置之前一直被A1占领,以此递推直到Ai,没有碎片要放在Ai的位置为止。 这种情况称为链。 2.基本上同1一样,不过,一开始的时候A0的原来位置并不是空的,而是最后的那个Ai占领着,这种情况称为环。 解决方法: 1。 对于1,只需要从A0开始一个一个按顺序放到原来的位置上即可。 2。 对于2,只需要从环中的任何一个节点开始,先将这个节点放到从尾部开始数起的空位,然后以链的方式处理,最后再将这个节点的数放回到最后一个节点的位置。 主要数据结构: q[i]: 放在第i个位上的数应该放在第q[i]个位上。 d[i]: 应该放在第i个位上的数,现在放在了第d[i]个位上。 #include usingnamespacestd; intn,k,tmp,t,index,pi; intq[10000]; intd[10000]; booloptneed; intmain(){ optneed=false; memset(q,-1,10000*sizeof(int)); memset(d,-1,10000*sizeof(int)); scanf("%d%d",&n,&k); intcounter=0; for(intj=0;j scanf("%d",&t); for(inti=0;i scanf("%d",&tmp); tmp--; q[tmp]=counter; d[counter]=tmp; counter++; } } //putnodeswhosecorrectplaceisemptyandsolvethechains. for(inti=0;i if(q[i]==i||q[i]==-1)continue; optneed=true; if(q[q[i]]==-1){ printf("%d%d\n",i+1,q[i]+1); q[q[i]]=q[i];q[i]=-1; index=i; while(d[index]! =-1){ printf("%d%d\n",d[index]+1,index+1); q[d[index]]=-1;q[index]=index; index=d[index]; } continue; } } if(optneed==true){ //solvetherings for(inti=0;i if(q[i]==i||q[i]==-1)continue; index=i; for(tmp=n-1;tmp>=0;tmp--)if(q[tmp]==-1)break; printf("%d%d\n",i+1,tmp+1); q[tmp]=q[i];q[index]=-1; while(index! =q[tmp]){ printf("%d%d\n",d[index]+1,index+1); q[index]=index;q[d[index]]=-1; index=d[index]; } printf("%d%d\n",tmp+1,index+1); q[index]=q[tmp];q[tmp]=-1; } }elseprintf("Nooptimizationneeded\n"); return0; } 本文来自CSDN博客,转载请标明出处: poj1129ChannelAllocation(图着色) 题意: 用中继器(repeater)给每个接受者(receiver)发送信号,为了防止信号干扰,两个相邻的广播站之间的中继器要不相同。 问至少需要多少个中继器。 这个问题相当于给定—个图,如果要求把所有顶点涂上颜色,使得相邻顶点具有不同的颜色,问最少需要几种不同的颜色。 经典的图着色问题。 思路: 根据给出的点构造邻接矩阵,顶点相邻的位置置1,不同的置0。 因为图着色问题颜色最多是四种颜色。 所以1种,2种,3种,4种,一个一个试,如果返回回来的着色方案总数不是0说明可行,为用的最少的颜色数。 #include #include #include #defineN27 intg[N][N],num,n; intx[N]; intok(intt) { inti; for(i=1;i<=n;i++) { if(i! =t) { if(g[t][i]==1&&x[i]==x[t]) return0; } } return1; } voidtraceback(intt,intm) { inti; if(t>n) { num++; } else { for(i=1;i<=m;i++) { x[t]=i; if(ok(t)) traceback(t+1,m); x[t]=0; } } } intmain() { inti,j; charch; while(scanf("%d",&n)&&n) { memset(g,0,sizeof(g)); ch=getchar(); for(i=1;i<=n;i++) { ch=getchar(); ch=getchar(); while(isalpha(ch=getchar()))//输入这里要注意 { g[i][ch-'A'+1]=1; g[ch-'A'+1][i]=1; } } for(j=1;j<=4;j++) { num=0; traceback(1,j); if(num! =0) { if(num==1) printf("1channelneeded.\n");//还有这里 else printf("%dchannelsneeded.\n",j); break; } } } return0; } POJ2049FindingNemo 题目不复杂,其实就是走迷宫,但是通过题的描述去确定一些参数来描述当前的迷宫是得想想。 而且有些细节你也得考虑,你要考虑整个迷宫的形状,要考虑初始的时候可行的迷宫入口,迷宫的入口有时是有门,有些是没门的~~~~下面是我的代码,写得个人觉得是相当冗繁,ANYWAY,总算解决了。 题目的思路是利用广搜,从可行的迷宫入口开始去搜索下一层的新的可行节点。 记录已经搜索到的节点的所通过的门数,一搜到Nemo的位置时立马返回该节点的所通过的门数,这时所通过的门数是最少的(这里大家可以思考一下为什么)。 #include #include #defineMAX_N210//最大限度边界 usingnamespacestd; intv[MAX_N+1][MAX_N+1];//v[i][j]表示到格子[i][j]的最小步骤数 intround[MAX_N+1][MAX_N+1][4];//记录当前格子四面边界的类型,0: air1: wall2: door intwn,dn,startXI,startYI,minSteps;//wn: 墙的数目,dn: 门的数目,起始点对应的格子坐标 doublestartXF,startYF;//起始点的输入浮点坐标 intdirarray[4][2]={{0,1},{0,-1},{-1,0},{1,0}};//方向数组,走四个方向对坐标的变化 //上: 0,下: 1,左: 2,右: 3 //入bfs队列的元素类型 structelem { //x,y记录这个格子的坐标;dir记录是从当前格子的哪个方向进入这个格子的,上: 0,下: 1,左: 2,右: 3 intx,y,dir,stepnum; //stepnum记录到达当前格子所需的步骤数 }; queue //取当前方向的对面方向 voidchangeDir(intorgignal,int&newDir) { if(orgignal==0)newDir=1; elseif(orgignal==1)newDir=1; elseif(orgignal==2)newDir=3; elsenewDir=2; } //当断当前坐标是否在合法范围内 boolinRange(intx,inty) { returnx>=0&&x<=205&&y>=0&&y<=205; } voidbfs() { //将Demo的位置入队列作为bfs的起始位置 while(! bfsq.empty())bfsq.pop(); elemcurelem,newelem; curelem.x=startXI;curelem.y=startYI;curelem.dir=-1;curelem.stepnum=0; v[startXI][startYI]=0; bfsq.push(curelem); intcurx,cury,curdir,cursteps,newx,newy,newdir,newsteps,d; while(! bfsq.empty()) { curelem=bfsq.front(); bfsq.pop(); curx=curelem.x;cury=curelem.y;curdir=curelem.dir;cursteps=curelem.stepnum; //到达出发点 if(curx==0&&cury==0) { //更新所需位置的最优值 if(cursteps minSteps=cursteps; continue; } //遍历当前格子的四个方向,尝试往这四个方向走 for(d=0;d<4;d++) { //不能往回走 if(d! =curdir) { //所走方向不能是墙 if(round[curx][cury][d]! =1) { //得到新的格子坐标 newx=curx+dirarray[d][0]; newy=cury+dirarray[d][1]; //新坐标在合法范围内 if(inRange(newx,newy)) { //计算所有方向相对目标格子所在的方位 changeDir(d,newdir); //门,步骤数+1 if(round[curx][cury][d]==2) newsteps=cursteps+1; else//空气,步骤数不变 newsteps=cursteps; //判断这个新格子的新状态是否需要入队列 if((v[newx][newy]==0xbf||newsteps { v[newx][newy]=newsteps; newelem.x=newx;newelem.y=newy;newelem.stepnum=newsteps;newelem.dir=newdir; bfsq.push(newelem); } } } } } } } intmain() { inti,j,x,y,d,t; while(scanf("%d%d",&wn,&dn)&&! (wn==-1&&dn==-1)) { minSteps=INT_MAX; memset(v,12,sizeof(v)); memset(round,0,sizeof(round)); for(i=1;i<=wn;i++) { scanf("%d%d%d%d",&x,&y,&d,&t); //输入的预处理,将线段(墙)转换为相应格子对应的四面边界 if(d==1) for(j=y+1;j<=y+t;j++) round[x][j][2]=round[x-1][j][3]=1; else for(j=x;j round[j][y][0]=round[j][y+1][1]=1; } for(i=1;i<=dn;i++) { scanf("%d%d%d",&x,&y,&d); //输入的预处理,将线段(门)转换为相应格子的四面边界方向 if(d==1) round[x][y+1][2]=round[x-1][y+1][3]=2; else round[x][y][0]=round[x][y+1][1]=2; } scanf("%lf%lf",&startXF,&startYF); //将Demo的位置转换为格子坐标 startXI=startXF; startYI=startYF+1; //题目中的异常数据 if(startXI<0||startXI>199||startYI<0||startYI>199) printf("0\n"); else { bfs(); if(minSteps==INT_MAX)printf("-1\n"); elseprintf("%d\n",minSteps); } } return0; } POJ2056TheSeparatorinGrid 这道题我曾经是想过深搜的,就一条线路一条线路的搜,不过后来,事实证明,所需的时间很多,老是TLE,弄得我都烦了,后来实在没办法之下,重新考虑题意。 其实对于i层的点来说,它只需知道i-1层的哪些点可以作为起始点和到达这些点所需的最小的sep(shortforseparator),这样算法的效率就跟层数相关,也跟每一层寻找起始点的操作相关,应该是O(M×N),而n<200,所以算法的效率是有保障的。 广搜 #include #include structNode { intx; inty; }; intchessBox[200][200];//标记每一点是'M','S'还是'B' intstep[200][200];//标记每一点到达时的最小步数 Nodestatus[200*200];//标记哪些节点是可继续展开(也就是可以形成separator的点)的节点 intM,N; intlast; voidFindNext(NodecurStatus)//从curStatus去寻找下面的可用节点 { intcurStep=step[curStatus.x][curStatus.y]; intm=curStatus.x+1; intn=curStatus.y; if(m==N-1)//如果是已经来到了倒数第二层,由于最后一层肯定只有一个separator的节点。 如果它有两个或以上,可能证明其中一个删去后它仍是一个sep.这样对于每个可用节点,我们直接考虑它是否可以直接向下一步形成seq. { if(chessBox[m][n]) { inttempStep=curStep+1; step[m][n]=tempStep; } } else { while(chessBox[m][n]==2)//如果不是倒数第二层,就向左展开可用节点可用的节点满足: 它的正下方有节点存在,如果这个节点是可继续延展的节点。 { if(chessBox[m+1][n]==2) { inttempStep=curStep+curStatus.y-n+1; //更新到达该节点的最小需要的Sep数 if(step[m][n]==0) { step[m][n]=tempStep; (status[last]).x=m; (status[last]).y=n; ++last; } else { if(step[m][n]>tempStep) { step[m][n]=tempStep; } } } --n; } n=curStatus.y+1; //向右展开节点并更新 while(chessBox[m][n]==2) { if(chessBox[m+1][n]==2) { inttempStep=curStep+n-curStatus.y+1; if(step[m][n]==0) { step[m][n]=tempStep; (st
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- ACM 50 解题 搜索
![提示](https://static.bdocx.com/images/bang_tan.gif)