hdu 2425 Hiking Trip
最短路径
选拔赛的题目,当时一看就觉得做过类似的,马上写了个DP,怎么都WA,一直WA到结束。后来才想起来是个图论,求最短路
题意:给一个矩阵,T表示树,.表示沙,#表示路,都是可以走的,而且他们各自带有一个值,vt,vs,vp,走到这个点要算上他们带的值,@表示石头不能走。再给你起点的行列和终点的行列(行列都是从0开始标记的),每次你都可以往上下左右四个方向走,重复走回同一个点也行,问你从起点走到终点的花费最少,(其中,起点带的那个值不用算上)
首先读入原始的矩阵,把他们的值都记好,石头不能走,可以把它的值赋值为无穷,也可标记为-1,等下不需要用到它(代码中是标记为-1)
然后按行从左到右给所有点标号,从1标到n*m。因为一个点可以走向它上下左右四个方向,其实就是和它四个方向的点相连,所以我们把这n*m个点变为一个图,目的就是求起点到终点的最短路。另外建图的时候,是建无向边,而且一个点最多和四个点相连,所以用邻接表效率会更高。另外无向边拆为有向边建图的时候i--->j,边权应该是j这个点所带的值
写了两个代码,一个spfa求最短路,一个是dij的优先队列版
spfa
/* 1.现将矩阵转换为图 2.在图上运行最短路算法,用spfa或者优先队列优化(堆优化)的dij ‘T’, ‘.’, ‘#’, ‘@’ tree, sand, path and stone. VP, VS, VT path, sand, or tree */ #include <cstdio> #include <cstring> #include <queue> #define N 25*25 #define INF 0x3f3f3f3f using namespace std; struct edge { int u,v,w,next; }e[N*N]; int nume , vp,vs,vt , row,col , r1,c1,r2,c2; int first[N],d[N],a[25][25]; int xx[4]={-1,1,0,0} , yy[4]={0,0,-1,1}; //上下左右四个方向 void spfa(int s ,int t) { queue<int>q; bool inq[N]; memset(d,0x3f,sizeof(d)); d[s]=0; memset(inq,0,sizeof(inq)); inq[s]=1; q.push(s); while(!q.empty()) { int u,v,w; u=q.front(); q.pop(); inq[u]=0; for(int k=first[u]; k!=-1; k=e[k].next) { v=e[k].v; w=e[k].w; if(d[u]+w < d[v]) //松弛 { d[v]=d[u]+w; if(!inq[v]) { q.push(v); inq[v]=1; } } } } if(d[t]!=INF) printf("%d\n",d[t]); else printf("-1\n"); } void add(int u ,int v ,int w) { e[nume].u=u; e[nume].v=v; e[nume].w=w; e[nume].next=first[u]; first[u]=nume++; } void build() { int n1,n2,x,y; memset(first,-1,sizeof(first)); nume=0; for(int i=1; i<=row; i++) //枚举列 for(int j=1; j<=col; j++) //枚举行 if(a[i][j]!=-1) //不是石头的才建边 { n1=(i-1)*col+j; //当前点的标号 for(int k=0; k<4; k++) //枚举当前点的四个方向 { x=i+xx[k]; y=j+yy[k]; if(a[x][y]!=-1) //不是石头可以建边 { n2=(x-1)*col+y; add(n1,n2,a[x][y]); //添加一条有向边,边权就是n2点的值 } } } } void input() { for(int i=0; i<=col+1; i++) a[0][i]=a[row+1][i]=-1; //矩阵上下两行不可到达 for(int i=0; i<=row+1; i++) a[i][0]=a[i][col+1]=-1; //矩阵左右两列不可到达 scanf("%d%d%d",&vp,&vs,&vt); for(int i=1; i<=row; i++) { char tmp[25]; scanf("%s",tmp+1); for(int j=1; j<=col; j++) { if(tmp[j]=='T') a[i][j]=vt; else if(tmp[j]=='.') a[i][j]=vs; else if(tmp[j]=='#') a[i][j]=vp; else a[i][j]=-1; } } scanf("%d%d%d%d",&r1,&c1,&r2,&c2); ++r1; ++c1; ++r2; ++c2; } int main() { int Case=0; while(scanf("%d%d",&row,&col)!=EOF) { input(); //读入原始的矩阵 build(); //将矩阵转化为图 printf("Case %d: ",++Case); spfa((r1-1)*col+c1 , (r2-1)*col+c2); //最短路算法计算源点到汇点的距离 } return 0; }
dij+优先队列
/* 1.现将矩阵转换为图 2.在图上运行最短路算法,用spfa或者优先队列优化(堆优化)的dij ‘T’, ‘.’, ‘#’, ‘@’ tree, sand, path and stone. VP, VS, VT path, sand, or tree */ #include <cstdio> #include <cstring> #include <vector> #include <queue> #include <utility> #define N 25*25 #define INF 0x3f3f3f3f using namespace std; typedef pair<int,int>pii; struct edge { int u,v,w,next; }e[N*N]; int nume , vp,vs,vt , row,col , r1,c1,r2,c2; int first[N],d[N],a[25][25]; int xx[4]={-1,1,0,0} , yy[4]={0,0,-1,1}; //上下左右四个方向 void dij(int s, int t) { bool done[N]; priority_queue<pii,vector<pii>,greater<pii> >q; memset(d,0x3f,sizeof(d)); d[s]=0; memset(done,0,sizeof(done)); q.push(make_pair(d[s],s)); while(!q.empty()) { pii tmp=q.top(); q.pop(); int u,v,w; u=tmp.second; if(done[u]) continue; done[u]=1; for(int k=first[u]; k!=-1; k=e[k].next) { v=e[k].v; w=e[k].w; if(d[u]+w < d[v]) { d[v]=d[u]+w; q.push(make_pair(d[v],v)); } } } if(d[t]!=INF) printf("%d\n",d[t]); else printf("-1\n"); } void add(int u ,int v ,int w) { e[nume].u=u; e[nume].v=v; e[nume].w=w; e[nume].next=first[u]; first[u]=nume++; } void build() { int n1,n2,x,y; memset(first,-1,sizeof(first)); nume=0; for(int i=1; i<=row; i++) //枚举列 for(int j=1; j<=col; j++) //枚举行 if(a[i][j]!=-1) //不是石头的才建边 { n1=(i-1)*col+j; //当前点的标号 for(int k=0; k<4; k++) //枚举当前点的四个方向 { x=i+xx[k]; y=j+yy[k]; if(a[x][y]!=-1) //不是石头可以建边 { n2=(x-1)*col+y; add(n1,n2,a[x][y]); //添加一条有向边,边权就是n2点的值 } } } } void input() { for(int i=0; i<=col+1; i++) a[0][i]=a[row+1][i]=-1; //矩阵上下两行不可到达 for(int i=0; i<=row+1; i++) a[i][0]=a[i][col+1]=-1; //矩阵左右两列不可到达 scanf("%d%d%d",&vp,&vs,&vt); for(int i=1; i<=row; i++) { char tmp[25]; scanf("%s",tmp+1); for(int j=1; j<=col; j++) { if(tmp[j]=='T') a[i][j]=vt; else if(tmp[j]=='.') a[i][j]=vs; else if(tmp[j]=='#') a[i][j]=vp; else a[i][j]=-1; } } scanf("%d%d%d%d",&r1,&c1,&r2,&c2); ++r1; ++c1; ++r2; ++c2; } int main() { int Case=0; while(scanf("%d%d",&row,&col)!=EOF) { input(); //读入原始的矩阵 build(); //将矩阵转化为图 printf("Case %d: ",++Case); dij((r1-1)*col+c1 , (r2-1)*col+c2); } return 0; }