题目描述:
两只牛逃跑到了森林里。农夫John开始用他的专家技术追捕这两头牛。你的任务是模拟他们的行为(牛和John)。
追击在10x10的平面网格内进行。一个格子可以是:
一个障碍物, 两头牛(它们总在一起), 或者 农民John. 两头牛和农民John可以在同一个格子内(当他们相遇时),但是他们都不能进入有障碍的格子。
一个格子可以是:
. 空地
* 障碍物
C 两头牛
F 农民John
这里有一个地图的例子:
*...*.....
......*...
...*...*..
..........
...*.F....
*.....*...
...*......
..C......*
...*.*....
.*.*......
牛在地图里以固定的方式游荡。每分钟,它们可以向前移动或是转弯。如果前方无障碍(地图边沿也是障碍),它们会按照原来的方向前进一步。否则它们会用这一分钟顺时针转90度。 同时,它们不会离开地图。
农民John深知牛的移动方法,他也这么移动。
每次(每分钟)农民John和两头牛的移动是同时的。如果他们在移动的时候穿过对方,但是没有在同一格相遇,我们不认为他们相遇了。当他们在某分钟末在某格子相遇,那么追捕结束。
读入十行表示农夫John,两头牛和所有障碍的位置的地图。每行都只包含10个字符,表示的含义和上面所说的相同,你可以确定地图中只有一个’F’和一个’C’.’F’和’C’一开始不会处于同一个格子中。
计算农夫John需要多少分钟来抓住他的牛,假设牛和农夫John一开始的行动方向都是正北(即上)。 如果John和牛永远不会相遇,输出0。
INPUT FORMAT
第1-10行:
每行10个字符,表示如上文描述的地图。
SAMPLE INPUT
*...*.....
......*...
...*...*..
..........
...*.F....
*.....*...
...*......
..C......*
...*.*....
.*.*......
OUTPUT FORMAT
输出一个数字,表示John需要多少时间才能抓住牛们。如果John无法抓住牛,则输出0。
SAMPLE OUTPUT
49
解题思路:
看到这一题的第一反应就是模拟,当农民和奶牛与之前的某一次状态相同时停止,但是这个明显是最容易想到的复杂度极高的方法,所以又手动模拟了一下,发现农民和奶牛一定有周期。然后决定通过数学的方法计算结果。先模拟一遍农民的走法,当出现循环时停止,然后模拟奶牛的走法。我们假设农民的周期为a,奶牛的周期为b,奶牛在第一次循环时步数为b1的时候到了农民a1时走过的位置。如果a1==b1,显然是结果。如果不相同,先判断a1时农民是否是循环阶段,这个很重要,我第一次时没注意,第9个点黑了。有公式:ax+a1=bx+b1=ans。则ax-by=b1-a1,如果a和b互质,则一定有解,否则当b1-a1为他们最大公约数的倍数时有解。
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#define INF 2147483647
int maze[12][12];//地图
int fway[12][12][4],cway[12][12][4];//用于记录以一方向再次到达此点时的步数
int fcount=1,ccount=1;//记录步数
int fTras=0,cTras=0,fTstart=0,cTstart=0;//记录周期及周期起始步数(cTstart多余)
int f[3],c[3];//0,1用来记位置,2用来记方向
int direction[4][2]={{-1,0},{0,1},{1,0},{0,-1}};//分别为北,东,南,西
int ans=INF;//记录答案
//这一题用暴搜也应该不会超时,由于一定有周期,所以循环两个周期的最小公倍数次也可以解,我为了降低复杂度,将后面循环的时间用数学公式计算了
int min(int a,int b){//返回最小值
return a<b?a:b;
}
int gcd(int a,int b){//求最大公约数
return b?gcd(b,a%b):a;
}
void print(){//用于调用观察,没有写在main函数里
int i,j;
for(i=0;i<12;i++){//输出地图
for(j=0;j<11;j++)
printf("%d ",maze[i][j]);
printf("%d\n",maze[i][j]);
}
printf("\n");
for(i=0;i<12;i++){//输出奶牛向北的行径路线
for(j=0;j<11;j++)
printf("%d\t",cway[i][j][0]);
printf("%d\n",cway[i][j][0]);
}
printf("\n");
for(i=0;i<12;i++){//输出奶牛向东的行径路线,以此类推
for(j=0;j<11;j++)
printf("%d\t",cway[i][j][1]);
printf("%d\n",cway[i][j][1]);
}
printf("\n");
for(i=0;i<12;i++){
for(j=0;j<11;j++)
printf("%d\t",cway[i][j][2]);
printf("%d\n",cway[i][j][2]);
}
printf("\n");
for(i=0;i<12;i++){
for(j=0;j<11;j++)
printf("%d\t",cway[i][j][3]);
printf("%d\n",cway[i][j][3]);
}
printf("\n");
for(i=0;i<12;i++){
for(j=0;j<11;j++)
printf("%d\t",fway[i][j][0]);
printf("%d\n",fway[i][j][0]);
}
printf("\n");
for(i=0;i<12;i++){
for(j=0;j<11;j++)
printf("%d\t",fway[i][j][1]);
printf("%d\n",fway[i][j][1]);
}
printf("\n");
for(i=0;i<12;i++){
for(j=0;j<11;j++)
printf("%d\t",fway[i][j][2]);
printf("%d\n",fway[i][j][2]);
}
printf("\n");
for(i=0;i<12;i++){
for(j=0;j<11;j++)
printf("%d\t",fway[i][j][3]);
printf("%d\n",fway[i][j][3]);
}
}
void fbfs(){//farmer行进路线,循环时停止
while(1){
fcount++;
if(maze[f[0]+direction[f[2]][0]][f[1]+direction[f[2]][1]]==0){//识别进行下一步时f的状态,判断是否撞墙
f[2]=(f[2]+1)%4;
}else{
f[0]=f[0]+direction[f[2]][0];
f[1]=f[1]+direction[f[2]][1];
}
if(fway[f[0]][f[1]][f[2]]){
fTras=fcount-fway[f[0]][f[1]][f[2]];
fTstart=fway[f[0]][f[1]][f[2]];
break;
}else{
fway[f[0]][f[1]][f[2]]=fcount;
}
}
}
void cbfs(){//cow行进路线,循环时停止
int flag=0;//判断是否在c做循环时相遇
while(1){
ccount++;
if(flag) break;
if(maze[c[0]+direction[c[2]][0]][c[1]+direction[c[2]][1]]==0){//识别进行下一步时c的状态
c[2]=(c[2]+1)%4;
}else{
c[0]=c[0]+direction[c[2]][0];
c[1]=c[1]+direction[c[2]][1];
}
if(cway[c[0]][c[1]][c[2]]){
cTras=ccount-cway[c[0]][c[1]][c[2]];
cTstart=cway[f[0]][f[1]][f[2]];
break;
}else{
cway[c[0]][c[1]][c[2]]=ccount;
}
for(int i=0;i<4;i++)//如果c做循环时遇到结果了,直接返回
if(fway[c[0]][c[1]][i]==ccount){
ans=ccount;
flag=1;
break;
}
}
}
int count(int a,int b,int a1,int b1){//ax+a1=by+b1=k,所以ax-by=b1-a1
int temp,c;
if(a1>b1){
temp=a;a=b;b=temp;
temp=a1;a1=b1;b1=temp;
}
c=b1-a1;
if(gcd(fTras,cTras)>1){//如果两个周期值的最大公约数不为1,只有当b1-a1为gcd(a,b)倍数时才有解
if(c%gcd(fTras,cTras)!=0)return INF;
}
int atemp=c/a,btemp=0;
while(1){//我用了比较笨的方法来算何时相等 = =
if(a*atemp-b*btemp==c)return a*atemp+a1;
if(a*atemp-b*btemp<c){
atemp++;
}
if(a*atemp-b*btemp>c){
btemp++;
}
}
}
void countmin(){//搜索两者在同一格的步数并计算,注意要在f循环后
for(int i=1;i<11;i++){
for(int j=1;j<11;j++){
for(int k=0;k<4;k++){
for(int l=0;l<4;l++){
if(fway[i][j][k]&&cway[i][j][l]&&fway[i][j][k]>fTstart){
ans=min(ans,count(fTras,cTras,fway[i][j][k],cway[i][j][l]));
}
}
}
}
}
}
int main(){
FILE *fin = fopen ("ttwo.in", "r");
FILE *fout = fopen ("ttwo.out", "w");
int i,j;
for(i=1;i<=10;i++){
for(j=1;j<=10;j++){
char a;
fscanf(fin,"%c",&a);
if(a=='.'||a=='F'||a=='C') maze[i][j]=1;
if(a=='F') f[0]=i,f[1]=j,fway[i][j][0]=1;
if(a=='C') c[0]=i,c[1]=j,cway[i][j][0]=1;
}
fscanf(fin,"\n");
}
fbfs();
cbfs();
printf("%d %d\n",fTras,cTras);
if(ans!=INF)fprintf(fout,"%d\n",ans-1);
else{
countmin();
if(ans==INF)fprintf(fout,"%d\n",0);
else fprintf(fout,"%d\n",ans-1);
}
exit(0);
}