以hdu1385为例子。http://acm.hdu.edu.cn/showproblem.php?pid=1385
一:floyed+路径输出
我们除了用dis[][]存储个点之间的距离之外,我们还需要用一个path[][]二维数组记录路径。开始看了网上有很多path记录的方法。
- 用path[i][j]记录终点j的(其实实现不了)
- 用path[i][j]记录终点j的前一个结点(开始自己用的就是这种方法,但是不知道错哪里了)
- 用path[i][j]记录i结点的后面的第一个结点。
我们采用的是第三种方法
初始化:
memset(path,-1,sizeof(path));
for(int i=1;i<=N;i++){
for(int j=1;j<=N;j++){
scanf("%d",&dis[i][j]);
if(-1==dis[i][j])
dis[i][j]=inf;
else
path[i][j]=j;
}
}
更新
//这个是普遍的写法,hdu1385还需要在此基础上加点东西。
for(res k=1;k<=N;k++){
for(res i=1;i<=N;i++){
for(res j=1;j<=N;j++){
if(dis[i][j]>dis[i][k]+dis[k][j]+cost[k]){
dis[i][j]=dis[i][k]+dis[k][j]+cost[k];
path[i][j]=path[i][k];
}
}
}
}
//思考:为甚麽不是path[i][j]=k呢?
//因为path[i][k]记录的是i到k路径中i后面的第一个结点。path[i][j]现在有更短的路可以到达
//就是通过i——K——j,那么当前path[i][j]就要更新为新路径中i后面的第一个结点
hdu1385ac代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define res register int
const int maxn=1005;
int dis[maxn][maxn];
int N,cost[maxn];
int path[maxn][maxn];
void floyed()
{
for(res k=1;k<=N;k++){
for(res i=1;i<=N;i++){
for(res j=1;j<=N;j++){
if(dis[i][j]>dis[i][k]+dis[k][j]+cost[k]){
dis[i][j]=dis[i][k]+dis[k][j]+cost[k];
path[i][j]=path[i][k];
}else if(dis[i][j]==dis[i][k]+dis[k][j]+cost[k]&&path[i][j]>path[i][k]){
path[i][j]=path[i][k];
}
}
}
}
}
void init()
{
memset(path,-1,sizeof(path));
for(int i=1;i<=N;i++){
for(int j=1;j<=N;j++){
scanf("%d",&dis[i][j]);
if(-1==dis[i][j])
dis[i][j]=inf;
else
path[i][j]=j;
}
}
for(int i=1;i<=N;i++)
scanf("%d",&cost[i]);
}
//void output(int i,int j)
//{
// if(-1==path[i][j]) return;
// output(i,path[i][j]);
// printf("%d-->",path[i][j]);
// if(i!=path[i][j]&&path[i][j]!=path[path[i][j]][j]) output(path[i][j],j);
//}
void output(int from,int to)
{
int u=from,v=to;
while(u!=v){
printf("%d-->",u);
u=path[u][v];
}
}
int main()
{
while(EOF!=scanf("%d",&N)&&N)
{
init();
floyed();
int from,to;
while(EOF!=scanf("%d%d",&from,&to)&&-1!=from&&-1!=to){
printf("From %d to %d :\n",from,to);
printf("Path: ");
output(from,to);
printf("%d\n",to);
printf("Total cost : %d\n\n",dis[from][to]);
}
}
return 0;
}
二:dijkstra+路径输出
这个采用的是记录前驱的方法。因为有多条路径,所以对于某一个结点,它是有多个前驱的。定义vector<int> pre[maxn],用不定长数组可以记录多个前驱。
对于路径长度的比较,我们可以这样:
然后我们定义记录后驱的数组,vector<int> next[maxn],将pre转换成每一个结点next,比如1有pre为2,那2就有next为1。
然后我们从起始结点开始深搜就行了,每次都选取序号最小的继续深搜,这样最后得到的就是字典序。
想的很美好,可惜下面的代码没ac。。。不知道错哪了,哪位大佬有空可以看看。
- if( !vis[j] && len[j]>len[p]+dis[p][j]+cost[j]),没有访问过,并且需要更新结点
之前存储的前驱就要clear掉,然后再push_back(p) - if(!vis[j]&&len[j]==len[p]+dis[p][j]+cost[j]),没有访问过,并且len[j]==len[p]+dis[p][j]+cost[j]
就push_back(p) (push_back(p)之后,就说明此时的j结点有多个前驱)
//wa的代码!!不知道错哪里了。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define res register int
const int maxn=1005;
int dis[maxn][maxn];
int N,cost[maxn];
vector<int> pre[maxn];
vector<int> NEXT[maxn];
void change(int A,int B)//将pre转换成NEXT
{
if(A==B) return;
else{
for(res i=0;i<pre[B].size();i++){
int n=pre[B][i];
NEXT[n].push_back(B);
change(A,n);
}
}
}
int dijkstra(int A,int B)
{
for(int i=1;i<=N;i++){
pre[i].clear();
NEXT[i].clear();
}
int vis[maxn],len[maxn];
memset(vis,0,sizeof(vis));
for(res i=1;i<=N;i++){
len[i]=min(inf,dis[A][i]+cost[A]+cost[i]);
if(inf!=dis[A][i]) pre[i].push_back(A);
}
pre[A].clear();
len[A]=cost[A];
vis[A]=1;
for(res i=1;i<N;i++){
int minc=inf,p=-1;
for(res j=1;j<=N;j++){
if(!vis[j]&&len[j]<minc){
minc=len[j];
p=j;
}
}
if(inf==minc) break;
vis[p]=1;
for(res j=1;j<=N;j++){
if(!vis[j]&&len[j]>len[p]+dis[p][j]+cost[j]){
len[j]=len[p]+dis[p][j]+cost[j];
pre[j].clear();
pre[j].push_back(p);
}else if(!vis[j]&&len[j]==len[p]+dis[p][j]+cost[j]){
res k;
for(k=0;k<pre[j].size();k++){
if(p==pre[j][k]) break;
}
if(pre[j].size()==k)
pre[j].push_back(p);
}
}
}
change(A,B);
// for(res i=1;i<=N;i++){
// printf("%d :",i);
// for(res j=0;j<pre[i].size();j++){
// printf("%d ",pre[i][j]);
// }
// printf("\n");
// }
// for(res i=1;i<=N;i++){
// printf("%d :",i);
// for(res j=0;j<NEXT[i].size();j++){
// printf("%d ",NEXT[i][j]);
// }
// printf("\n");
// }
return len[B];
}
void init()
{
for(int i=1;i<=N;i++){
for(int j=1;j<=N;j++){
scanf("%d",&dis[i][j]);
if(-1==dis[i][j]){
dis[i][j]=inf;
}
}
}
for(int i=1;i<=N;i++)
scanf("%d",&cost[i]);
}
void output(int from,int to)
{
if(from==to){
printf("%d\n",to);
return;
}else{
int minc=inf;
for(int i=0;i<NEXT[from].size();i++){
if(minc>NEXT[from][i]){
minc=NEXT[from][i];
}
}
printf("%d-->",from);
output(minc,to);
}
}
int main()
{
while(EOF!=scanf("%d",&N)&&N)
{
init();
int from,to;
while(EOF!=scanf("%d%d",&from,&to)&&-1!=from&&-1!=to){
printf("From %d to %d :\n",from,to);
int sum=dijkstra(from,to);
printf("Path: ");
output(from,to);
printf("Total cost : %d\n\n",sum-cost[from]-cost[to]);
}
}
return 0;
}