这道题折腾了两天,记录一下。
- 思路1:
首先求出所有结点两两之间的最短路径,利用弗洛伊德算法,三个for循环,时间复杂度为n的三次方;
再利用深度优先遍历dfs求出从某一结点出发遍历完所以节点的最短路径;
最后对比各结点出发的最短路径,得出遍历所以结点的最短路径。
代码如下:
bool visitAll(int *flag, int size) { int i; for(i = 0; i < size; i++) { if(flag[i] == 0) return false; } return true; } void dfs(int path[12][12], int size, int node, int *val, int *ret, int *flag) { int i; //printf("node = %d\n",node); if(flag[node] == 1) return; else flag[node] = 1; if(visitAll(flag, size) && *ret > *val) { *ret = *val; printf("*****ret = %d*****\n",*ret); } for(i = 0; i < size; i++) { if(flag[i] == 0) { //printf("i = %d val = %d\n",i,*val); *val += path[node][i]; dfs(path, size, i, val, ret, flag); *val -= path[node][i]; } } flag[node] = 0; } int shortestPathLength(int** graph, int graphRowSize, int *graphColSizes) { int i,j,k; int path[12][12]; int flag[12] = {0}; //record whether the node is visited, 0 represent not visit, 1 represent visit. int ret = 1000; int val = 0; for(i = 0; i < graphRowSize; i++) { for(j = 0; j < graphRowSize; j++) path[i][j] = 1000; } for(i = 0; i < graphRowSize; i++) { for(j = 0; j < graphColSizes[i]; j++) { path[i][i] = 0; path[i][graph[i][j]] = 1; } } for(k = 0; k < graphRowSize; k++) for(i = 0; i < graphRowSize; i++) for(j = 0; j < graphRowSize; j++) if(path[i][j] > path[i][k] + path[k][j]) path[i][j] = path[i][k] + path[k][j]; for(i = graphRowSize-1; i >= 0; i--) { val = 0; dfs(path, graphRowSize, i, &val, &ret, flag); printf("i = %d ret = %d\n",i,ret); } return ret; }
【注】这种思路,需要注意一点,从一个结点出发寻找最短路径时,并不是第一次将所有结点都访问完就可以,需要将所有可能情况都遍历一次,对比所有结果,获取最短路径。
比如状态图输入为以下数组时,
[[1,2,3],[0],[0],[0]]
所有结点两两之间最短路径数组path如下:
结点0 | 结点1 | 结点2 | 结点3 | |
结点0 | 0 | 1 | 1 | 1 |
结点1 | 1 | 0 | 2 | 2 |
结点2 | 1 | 2 | 0 | 2 |
结点3 | 1 | 2 | 2 | 0 |
由于两两结点间总是存在路径,因此从某一节点出发会出现多条遍历路径,以上述状态为例,从结点0出发,第一个遍历路径为 0 - 1 - 2 - 3 , 对应访问路径为 1 + 2 + 2 = 5 也可以是0 - 1 - 3 - 2、0 - 2 - 1 - 3等等。必须将所有情况都遍历一次,才能获取到从某一个节点出发的最短路径。因此这种方法的时间复杂度为n!结果是TLE(Time Limit Exceeded)。
- 思路二:
/****************************************************************************** ** 先求出每对定点之间的最短距离 利用弗洛伊德算法 记录在path[i][j]数组中 ** 再利用一个数组记录每个可能的访问状态需要经过的最短路径 visit[1<<n][n] n为节点个数 其中数组行下标代表节点被访问的状态 1代表被访问 0代码没有被访问 ** 如6的二进制表示为0110 表示第二个节点和第三个节点被访问 其余两个节点没有被访问的状态 数组列下标表示从该节点开始访问 数组值存储这种状态的最短路径 ** 数组行下标为0时 代表所有节点都没有被访问 因此第一行数组值为0 之后的每个数组值都需要借助前面的状态和path数组 ** 以6为例 0110 只有第2和第3个节点被访问 因此从第1个节点和第4个节点出发没有路径 因此相应列的数组值设为最大值10000 ** 从第2个节点开始 第二个节点首先被访问 那么剩下的状态就变为0100 查看数组visit[0100]的值 并结合数组path进行判断 找到从节点j出发到达状态0100的最小路径 ** 赋值给visit[0110][j] ** 最后一行为所以节点都被访问的状态 依次对比从节点j出发访问完所以节点需要的路径 将最小路径返回 *******************************************************************************/ int shortestPathLength(int** graph, int graphRowSize, int *graphColSizes) { int i,j,k; int path[12][12]; int ret = 1000; int n = 1<<graphRowSize; int visit[1<<12][12]; int min; int val; for(i = 0; i < graphRowSize; i++) { for(j = 0; j < graphRowSize; j++) path[i][j] = 1000; } memset(visit[0],0,graphRowSize); for(i = 1; i < n; i++) { for(j = 0; j < graphRowSize; j++) visit[i][j] = 10000; } for(i = 0; i < graphRowSize; i++) { for(j = 0; j < graphColSizes[i]; j++) { path[i][i] = 0; path[i][graph[i][j]] = 1; } } for(k = 0; k < graphRowSize; k++) for(i = 0; i < graphRowSize; i++) for(j = 0; j < graphRowSize; j++) if(path[i][j] > path[i][k] + path[k][j]) path[i][j] = path[i][k] + path[k][j]; for(i = 1; i < n; i++) { for(j = 0; j < graphRowSize; j++) { if(i == 1<<j) { visit[i][j] = 0; continue; } if(i & 1<<j) { //printf("i = %d j = %d\n",i,j); for(k = 0; k < graphRowSize; k++) { if(j != k) { min = visit[i][j]; val = visit[i ^ 1<<j][k] + path[j][k]; if(min > val) visit[i][j] = val; } } } } } for(i = 0; i < graphRowSize; i ++) { val = visit[n-1][i]; if(ret > val) ret = val; } return ret; }