首先介绍一下什么是奇偶剪枝:
奇偶剪枝
是数据结构的搜索中,剪枝的一种特殊小技巧。 现假设起点为(sx,sy),终点为(ex,ey),给定t步恰好走到终点,
如图所示(“|”竖走,“—”横走,“+”转弯),易证abs(ex-sx)+abs(ey-sy)为此问题类中任意情况下,起点到终点的最短步数,记做step,此处step1=8;
如图,为一般情况下非最短路径的任意走法举例,step2=14; step2-step1=6,偏移路径为6,偶数(易证); 故,
若t-[abs(ex-sx)+abs(ey-sy)]结果为非偶数(奇数),则无法在t步恰好到达; 返回,false; 反之亦反。
下面是我复制的一些网上的资料没怎么看懂
路径奇偶条件剪枝
仔细观察,从起点到终点的距离的奇偶性决定了所有从起点到终点的可行路径距离的奇偶性!而且这种奇偶相关不随搜索状态的变化而变化。
可以把map看成这样:
010101
101010
010101
101010
010101这里我们用0代表偶数步用1代表积数步
从为0的格子走一步,必然走向为1的格子
从为1的格子走一步,必然走向为0的格子
即:
0->1或1->0不管怎么走必然是奇数步
0->0走1->1不管怎么走必然是偶数步
所以我们可以把其看成最短路径的时候情况用来判断从一点到另一点是用积数步走完还是偶数步走完
很明显,如果起点在0而终点在1那显然要经过奇数步才能从起点走到终点,依次类推,奇偶相同的偶数步,奇偶不同的奇数步
在读入数据的时候就可以判断,并且做剪枝,当然做的时候并不要求把整个矩阵0,1刷一遍,读入的时候起点记为(Si,Sj)终点记为(Di,Dj)判断(Si+Sj)和(Di+Dj)的奇偶性就可以了。
所以通过分析,可以得到下面这一判定条件,网上有好多别的式子,但本质是一样的。
if(abs(P.x-Q.x)+abs(P.y-Q.y)%2==T%2)dfs(P);//只需判断一次即可
即如果用xy表示当前所在的迷宫位置时xxyy表示出口时t表示规定的要用多少步走出迷宫count表示已经走过的步数那么t-count是剩下要走的步数那么满足下面的式子才能够在t步正好走出迷宫注易是正好走出哈
abs(xx-x)+abs(yy-y))%2!=(t-count)%2
这个剪枝是很强大的、概率上来说可以减掉一半的Case
下面举例题:
TempteroftheBone
TimeLimit:2000/1000MS(Java/Others)MemoryLimit:65536/32768K(Java/Others)
TotalSubmission(s):24023AcceptedSubmission(s):6616
ProblemDescription
Thedoggiefoundaboneinanancientmaze,whichfascinatedhimalot.However,whenhepickeditup,themazebegantoshake,andthedoggiecouldfeelthegroundsinking.Herealizedthatthebonewasatrap,andhetrieddesperatelytogetoutofthismaze.
ThemazewasarectanglewithsizesNbyM.Therewasadoorinthemaze.Atthebeginning,thedoorwasclosedanditwouldopenattheT-thsecondforashortperiodoftime(lessthan1second).ThereforethedoggiehadtoarriveatthedooronexactlytheT-thsecond.Ineverysecond,hecouldmoveoneblocktooneoftheupper,lower,leftandrightneighboringblocks.Onceheenteredablock,thegroundofthisblockwouldstarttosinkanddisappearinthenextsecond.Hecouldnotstayatoneblockformorethanonesecond,norcouldhemoveintoavisitedblock.Canthepoordoggiesurvive?Pleasehelphim.
Input
Theinputconsistsofmultipletestcases.ThefirstlineofeachtestcasecontainsthreeintegersN,M,andT(1<N,M<7;0<T<50),whichdenotethesizesofthemazeandthetimeatwhichthedoorwillopen,respectively.ThenextNlinesgivethemazelayout,witheachlinecontainingMcharacters.Acharacterisoneofthefollowing:
'X':ablockofwall,whichthedoggiecannotenter;
'S':thestartpointofthedoggie;
'D':theDoor;or
'.':anemptyblock.
Theinputisterminatedwiththree0's.Thistestcaseisnottobeprocessed.
Output
Foreachtestcase,printinoneline"YES"ifthedoggiecansurvive,or"NO"otherwise.
SampleInput
445S.X...X...XD....345S.X...X....D000
SampleOutput
NOYES
我的代码:
#include<stdio.h>
#include<string.h>
#include<math.h>
intn,m,t,s_i,s_j,d_i,d_j;
chara[10][10];
intflag[10][10];
intans,cnt;
voiddfs(inti,intj,intti)
{
if(ans==1)return;
if(i<=0||j<=0||i>n||j>m)return;
if(ti>t)return;
if(a[i][j]=='X')return;
//if(t-ti<abs(d_i-i)+abs(d_j-j))return;//剩余最短路径所需时间如果比剩余时间大则return
//上面那句话加上可以更加缩短时间
if(flag[i][j]==1)return;
if(t==ti&&i==d_i&&j==d_j){ans=1;return;}
if(abs(t-ti)%2!=(abs(d_i-i)+abs(d_j-j))%2)return;//奇偶剪枝如果少了这句就会超时所以
//这句话的作用很大要好好体会
flag[i][j]=1;
dfs(i-1,j,ti+1);
dfs(i+1,j,ti+1);
dfs(i,j+1,ti+1);
dfs(i,j-1,ti+1);
flag[i][j]=0;
}
intmain()
{
inti,j;
while(scanf("%d%d%d",&n,&m,&t))
{
getchar();
cnt=0;
if(n==0&&m==0&&t==0)return0;
for(i=1;i<=n;i++)
{
for(j=1;j<=m;j++)
{
scanf("%c",&a[i][j]);
if(a[i][j]=='S'){s_i=i;s_j=j;}
else
if(a[i][j]=='D'){d_i=i;d_j=j;cnt++;}//一开始这里的cnt++忘了加了导致错了//好多次浪费了好多时间所以以后做题小细节一定要注意到不要忽视任何一个小地
//方
elseif(a[i][j]=='.')cnt++;
}
getchar();
}
if(cnt<t){printf("NO\n");continue;}
memset(flag,0,sizeof(flag));
ans=0;
dfs(s_i,s_j,0);
if(ans==1)printf("YES\n");
elseprintf("NO\n");
}
return0;
}