总时间限制: 1000ms 内存限制: 65536kB
描述
“我是要成为海贼王的男人!”,路飞一边喊着这样的口号,一边和他的伙伴们一起踏上了伟大航路的艰险历程。
路飞他们伟大航路行程的起点是罗格镇,终点是拉夫德鲁(那里藏匿着“唯一的大秘宝”——ONE PIECE)。而航程中间,则是各式各样的岛屿。
因为伟大航路上的气候十分异常,所以来往任意两个岛屿之间的时间差别很大,从A岛到B岛可能需要1天,而从B岛到A岛则可能需要1年。当然,任意两个岛之间的航行时间虽然差别很大,但都是已知的。
现在假设路飞一行从罗格镇(起点)出发,遍历伟大航路中间所有的岛屿(但是已经经过的岛屿不能再次经过),最后到达拉夫德鲁(终点)。假设他们在岛上不作任何的停留,请问,他们最少需要花费多少时间才能到达终点?
输入
输入数据包含多行。
第一行包含一个整数N(2 < N ≤ 16),代表伟大航路上一共有N个岛屿(包含起点的罗格镇和终点的拉夫德鲁)。其中,起点的编号为1,终点的编号为N。
之后的N行每一行包含N个整数,其中,第i(1 ≤ i ≤ N)行的第j(1 ≤ j ≤ N)个整数代表从第i个岛屿出发到第j个岛屿需要的时间t(0 < t < 10000)。第i行第i个整数为0。
输出
输出为一个整数,代表路飞一行从起点遍历所有中间岛屿(不重复)之后到达终点所需要的最少的时间。
样例输入
样例输入1:
4
0 10 20 999
5 0 90 30
99 50 0 10
999 1 2 0
样例输入2:
5
0 18 13 98 8
89 0 45 78 43
22 38 0 96 12
68 19 29 0 52
95 83 21 24 0
样例输出
样例输出1:
100
样例输出2:
137
提示
提示:
对于样例输入1:路飞选择从起点岛屿1出发,依次经过岛屿3,岛屿2,最后到达终点岛屿4。花费时间为20+50+30=100。
对于样例输入2:可能的路径及总时间为:
1,2,3,4,5: 18+45+96+52=211
1,2,4,3,5: 18+78+29+12=137
1,3,2,4,5: 13+38+78+52=181
1,3,4,2,5: 13+96+19+43=171
1,4,2,3,5: 98+19+45+12=174
1,4,3,2,5: 98+29+38+43=208
所以最短的时间花费为137
单纯的枚举在N=16时需要14!次运算,一定会超时。
1 题目分析
将题目分析得到,我们需要解决的是,遍历整个图的所有节点,然后求出最小其权值的最小值。
2 解题思路
由于整张图中的所有点默认标号为1-16,所以我们可以采用深度优先遍历的方式来遍历整张图,然后记录下用时最短的那个时间即可。
3 存在的问题
题目中所给最大的编号是16,遍历整张图需要进行16!次操作,时间太长,绝对超时。
4 解决方案
利用深度优先的方式,在其中进行剪枝操作。
剪枝分为两种:1 可行性剪枝 2 最优性剪枝。在这里我们只用到后者。
剪枝1 如果在进行深度优先时已经花费的时间大于了已有的最优时间,那么不进行接下来的操作。
剪枝2 记录从出发点1-某个点i的状态,当两个状态相同时,如果前者花费的时间少,那么后者不需要再进行操作。
比如 1-4
状态1 :1 2 3 4 花费时间 :5
状态2 :1 3 2 4 花费时间 :8
由于比较时,前者是已经走完的,那么当后者花费时间比较大的时候,我们直接采取前者的路线即可,不需要再进行自己的操作。
5 难点
如何进行状态的存储?
利用二维数组State[i][j];i表示现在走到的岛屿编号,j代表已经走过的路线。
也许你会问j怎么代表路线呢?
我们利用二进制来存储已经走过的路线,走过就将对应位置设为1,比如:
3:11->走过1-2,i代表的则是现在停留的岛屿 也就是2
问题解决代码:
#include
#include
#include
#include
#include
using namespace std;
#define N 17
#define INF 1<<30
int MinTime;
int n;
//存储状态
int State[16][32800];//中间状态存储
bool IsLandSign[17];
int IsLand[N][N];
int ct;
int Min(int a,int b)
{
return a
}
void dfs(int IsLandNum,int AlreadyTime,int state)
{
int i;
IsLandSign[IsLandNum]=false;
ct++;
//返回条件
if(ct==n-1)
{
MinTime=Min(AlreadyTime+IsLand[IsLandNum][n],MinTime);
return;
}
//最优化剪枝
if(AlreadyTime>=MinTime)
return;
//中间状态最优化剪枝
if(AlreadyTime State[IsLandNum][state]=AlreadyTime; else { return; } for(i=1;i { if(IsLandSign[i]==true) { dfs(i,AlreadyTime+IsLand[IsLandNum][i],state+pow(2,i-1)); ct--; IsLandSign[i]=true; } } } int main() { //freopen("in.txt","r",stdin); int i,j; while(~scanf("%d",&n)) { for(i=1;i<=n;i++) { for(j=1;j<=n;j++) { scanf("%d",&IsLand[i][j]); } } memset(IsLandSign,true,sizeof(IsLandSign)); memset(State,-1,sizeof(State)); ct=0; MinTime=1<<30; dfs(1,0,1); cout< } return 0; }