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;
}

 

posted @ 2013-03-09 21:18  Titanium  阅读(312)  评论(0编辑  收藏  举报