题目描述
:路飞他们伟大航路行程的起点是罗格镇,终点是拉夫德鲁
---------------因为伟大航路上的气候十分异常,所以来往任意两个岛屿之间的时间差别很大,从A岛到B岛可能需要1天,而从B岛到A岛则可能需要1年。
---------------当然,任意两个岛之间的航行时间虽然差别很大,但都是已知的。
现在假设路飞一行从罗格镇(起点)出发,遍历伟大航路中间所有的岛屿(但是已经经过的岛屿不能再次经过),最后到达拉夫德鲁(终点)。假设他们在岛上不作任何的停留,请问,他们最少需要花费多少时间才能到达终点?
思路
DFS,寻找最小时间,重点是把岛的连接顺序写明白
剪枝比较好想,预判时间,是否走过
要走的第一个岛和最后一个岛都是固定的
WA代码(第一次写出来的
#include<stdio.h>
#include<cmath>
#include<cstring>
using namespace std;
int island[8][8];
int mintime =1<<30;
int roadtime,islandN;
int visited[8];
void dfs(int i,int j,int num){
if(visited[i])//已走过
return;
if(num==islandN-1){
roadtime+=island[i][islandN];//最后一个岛固定的
mintime=min(roadtime,mintime);
}
if(roadtime+island[i][j]>mintime)//预判 不应该是island[i][j]
return;
for(;i<islandN;++i){
for(;j<islandN;++j){//!就是这里没有考虑岛屿的顺序关系导致 W A
visited[i]=1;
roadtime+=island[i][j];
dfs(i,j,num++);
roadtime-=island[i][j];
visited[i]=0;
}
}
}
int main(){
scanf("%d",&islandN);
for(int i=0;i<islandN;++i){
for(int j=0;j<islandN;++j)
scanf("%d",&island[i][j]);
printf("\n");
}
memset(visited,0,sizeof(visited));
int num=0;
dfs(0,0,num);
printf("%d",mintime);
}
return 0;
}
WA分析
在DFS的循环里,我的代码在判断一个岛有没有走过的时候,没有考虑连接关系
visited [ i ][ j ] = 1 ;只是把这个岛标记成了走过,并不能判断是从哪个岛来到这个岛的
剪枝的时候顺序也有问题
输入案例,结果为30,10+20+0=30;全部为第一个岛的时间关系
AC代码
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
using namespace std;
int island[20][20];
int mintime=1<<30;//记录最短的路径
int totaltime=0;//记录目前为止总共走了多少
int visited[20];//记录第i个岛屿是否走过
int n;
int guo=0;//记录走过了哪些岛屿 第i个岛屿如果走过则guo中加上2的i次方
int time[65536][20];//time[i][j]记录已经走过i岛屿(i用二进制表示**所有**走过的岛屿)到达j的最短距离
void dfs(int s,int c){//s表示已经走过了s个岛屿(数量),c表示目前在岛屿c
if(s==n-1)
mintime=min(totaltime+island[c][n-1],mintime);//最后一个岛固定
for(int i=1;i<n-1;i++){
if(!visited[i]){//未走过
int q=guo+pow(2,i);//q:所有走过岛(包括i)的二进制表达,guo+=也可以,简写
if(totaltime+island[c][i]>=time[q][i])
continue;//减支搜索 全局范围 走到C岛比目前已知的最短时间长则无需继续搜索
time[q][i]=totaltime+island[c][i];//更新最小值
visited[i]=1;
guo+=pow(2,i);
totaltime+=island[c][i];
dfs(s+1,i);//表示岛之间的连接关系
totaltime-=island[c][i];
guo-=pow(2,i);//回溯
visited[i]=0;
}
}
}
int main(){
for(int i=0;i<65535;i++)
for(int j=0;j<20;j++)
time[i][j]=1<<30;
cin>>n;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
cin>>island[i][j];
memset(visited,0,sizeof(visited));
visited[0]=1;
guo+=1;
dfs(1,0);
cout<<mintiem<<endl;
return 0;
}
心得
- dfs函数传达了岛的顺序( s , c )
- time[ i ][ j ]二维函数,记录所有走过岛到 c岛 的最小时间,
超强剪枝,全局范围包含所有搜索情况 - 二进制表示不会发生数字相同的情况
- visited[ i ]表示在这个搜索分支中有哪些岛走过了
- 没错,看的又是别人的代码,自己只能WA,道阻且长