【HDNOIP】HD201404最短路径

HD201404最短路径

【试题描述】

a、b、c是3个互不相等的1位正数,用它们和数字0可以填满一个n行n列的方格阵列,每格中都有4种数码中的一个。填入0的格子表示障碍物,不能属于任何路径。你是否能找出一条从1行1列出发,到达n行n列且代价最小的路径呢?注意:每一格只能走向与之相邻的上、下、左、右的非0且不出界的格子。而所谓路径代价指的是路径经过的所有格子中的数字总和。请你编程求出从1行1列的位置出发到达n行n列的最小路径代价,若无法到达就输出-1。

【输入要求】

第一行输入数字n。
接下来的n行每行是一个长度为n的数字串,这n个字符串就构成了一个数字符的方阵。方阵中除了‘0‘外,最多还可以包含3种数字符。

【输出要求】

仅有最小代价或-1这一个整数。

【输入实例】

【输入样例1】
4
1231
2003
1002
1113
【输入样例2】
4
3150
1153
3311
0530

【输出实例】

【输出样例1】
10
【输出样例2】
-1

【其他说明】

60%的数据,n<10,80%的数据,n<100,100%的数据,n<1000

【试题分析】

第一眼:这不就是迷宫吗!?看我秒过!!

第二眼:还不太一样,迷宫是走一步ans++,这个是要加一个变量的

第三眼:上DFS代码吧!!

于是就花了1小时yy了这么一个代码:

#include<iostream>
#include<cstring>
using namespace std;
char map[1001][1001],cinm[1001][1001];
int a[1001][1001],ans=0,m,n,k[1001][1001],k1[1001][1001],mk=0;//k不变,变的只是k1.
void res(int u,int v,int i,int j)
{
    int t=k1[u][v];
    if(u==i&&v==j) ans=t,mk=1;
    if(v<m-1&&map[u][v+1]!=‘#‘&&a[u][v+1]>t+k[u][v+1])
    {
        k1[u][v+1]=k[u][v+1]+t;
		a[u][v+1]=k1[u][v+1];
        res(u,v+1,i,j);
    }
    if(u>0&&map[u-1][v]!=‘#‘&&a[u-1][v]>t+k[u-1][v])
    {
        k1[u-1][v]=k[u-1][v]+t;
		a[u-1][v]=k1[u-1][v];
        res(u-1,v,i,j);
    }
    if(v>0&&map[u][v-1]!=‘#‘&&a[u][v-1]>t+k[u][v-1])
    {
        k1[u][v-1]=k[u][v-1]+t;
		a[u][v-1]=k1[u][v-1];
        res(u,v-1,i,j);
    }
    if(u<n-1&&map[u+1][v]!=‘#‘&&a[u+1][v]>t+k[u+1][v])
    {
        k1[u+1][v]=k[u+1][v]+t;
		a[u+1][v]=k1[u+1][v];
        res(u+1,v,i,j);
    }
}
int main()
{
    cin>>n;
    m=n;
    for(int i=0;i<n;i++)
    {
        cin>>cinm[i];
        for(int j=0;j<n;j++)
        {
            k[i][j]=cinm[i][j]-‘0‘;
            k1[i][j]=k[i][j];
        }
	}
    int startx=0,starty=0,endx=n-1,endy=n-1;
    for(int i=0;i<n;i++)
        for(int j=0;j<m;j++)
		{
            if(i==startx&&j==starty) map[i][j]=‘S‘;
            else if(i==endx&&j==endy) map[i][j]=‘T‘;
            if(k[i][j]==0)map[i][j]=‘#‘;
            else map[i][j]=‘.‘;
        }
    memset(a,1,sizeof(a));
    a[0][0]=0;
    res(startx,starty,endx,endy);
    if(mk==1)cout<<ans<<endl;
    else cout<<-1;
}

结果没有看数据范围,1000!!?

结果对了,但是就是时间超了。

细细想来也没有神马办法优化,罢罢罢……

BFS:

好了,如果DFS时间超限,那么只能用BFS了。

于是又yy了一个BFS的代码:

#include<iostream>
using namespace std;
char Graph[1001][1001];
int n,m,startx,starty;
int Dir[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
int Vis[1001][1001];
int Dis[1001][1001];
char cinm[1001][1001],k[1001][1001];
int Fit(int x,int y)
{
    return (x>=1&&x<=n&&y>=1&&y<=m);
}
int BFS(int x,int y)
{
    int queue[100000];
    int i,head=0,tail=0;
    queue[tail++]=x;
    queue[tail++]=y;
    Vis[x][y]=1;
    while(head<tail)
	{
        int nowx=queue[head++];
        int nowy=queue[head++];
        for(i=0;i<4;i++)
		{
            int tmpx=nowx+Dir[i][0];
            int tmpy=nowy+Dir[i][1];
            if(Fit(tmpx,tmpy)&&tmpx==n&&tmpy==n&&Graph[tmpx][tmpy]==‘.‘) return Dis[nowx][nowy]+k[tmpx][tmpy];
            if(Fit(tmpx,tmpy)&&Graph[tmpx][tmpy]==‘.‘&&Dis[nowx][nowy]+k[tmpx][tmpy]<Dis[tmpx][tmpy])
			{
                Dis[tmpx][tmpy]=Dis[nowx][nowy]+k[tmpx][tmpy];
                queue[tail++]=tmpx;
                queue[tail++]=tmpy;
            }
        }
    }
    return 0;
}
int main()
{
    int i ,j ,startx=1,starty=1;
    scanf("%d",&n);
    m=n;
    for(int i=1;i<=n;i++)
    {
        scanf("%s",cinm[i]+1);
        for(int j=1;j<=n;j++)
            k[i][j]=cinm[i][j]-‘0‘,Dis[i][j]=999999;
	}
	Dis[1][1]=k[1][1];
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
		{
            if(k[i][j]==0)Graph[i][j]=‘#‘;
            else Graph[i][j]=‘.‘;
        }
    int ans=0;
    ans=BFS(startx,starty);
    if(ans!=0) printf("%d\n",ans);
    else printf("-1\n");
}
/*
7
3221000
3110012
3123031
1123301
2211123
0012333
3312001*/

  结果错误。

有些点的确能A,但是有些点死活A不了。

突然发现了这样一个条件:最多有三个除0之外不同的数,于是又yy了一个三个队列维护的代码:

#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
inline int read() {
    int x=0,f=1;char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c==‘-‘) f=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-‘0‘;
    return x*f;
}
char cinm[1001][1001];
int n,Dis[1001][1001],Vis[1001][1001],number[1001],timek,k[1001][1001];
struct P
{
    int x,y;
    bool operator < (const P& ths) {return Dis[x][y]<Dis[ths.x][ths.y];}
};
queue<P> que[3];
int getfront()
{
    int c=-1;
    if(que[0].size()) c=0;
    if(que[1].size()&&(c<0||que[1].front()<que[c].front())) c=1;
    if(que[2].size()&&(c<0||que[2].front()<que[c].front())) c=2;
    return c;
}
int Fit(int x,int y)
{
    return (x>=1&&x<=n&&y>=1&&y<=n);
}
int Dir[4][2]={{1,0},{0,1},{-1,0},{0,-1}};//方向数组
int BFS()
{
    if(k[1][1]==0||k[n][n]==0) return -1;
    que[number[cinm[1][1]]].push((P){1,1});
	Dis[1][1]=k[1][1];
    while(que[0].size()+que[1].size()+que[2].size())//当三个队列的大小和非空时
	{
        int t=getfront();
	    int x=que[t].front().x;
		int y=que[t].front().y;//获取坐标
		que[t].pop();
        if(x==n&&y==n) return Dis[x][y];
        if(Vis[x][y]==1) continue;
		Vis[x][y]=1;
        for(int i=0;i<=3;i++)
		{
            int nx=x+Dir[i][0];
			int ny=y+Dir[i][1];
            if(Fit(nx,ny)&&k[nx][ny]!=0&&Dis[x][y]+k[nx][ny]<Dis[nx][ny])
			{
                Dis[nx][ny]=Dis[x][y]+k[nx][ny];
                que[number[cinm[nx][ny]]].push((P){nx,ny});
            }
        }
    }
    return -1;
}
int main()
{
    n=read();
    for(int i=1;i<=n;i++) scanf("%s",cinm[i]+1);
    memset(number,-1,sizeof(number));
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
	    {
            if(number[cinm[i][j]]<0&&cinm[i][j]!=‘0‘) number[cinm[i][j]]=timek++;//为非0元素标号
            Dis[i][j]=99999999;
            k[i][j]=cinm[i][j]-‘0‘;
        }
    printf("%d\n",BFS());
}

【代码】

#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
inline int read() {
    int x=0,f=1;char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c==‘-‘) f=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-‘0‘;
    return x*f;
}
char cinm[1001][1001];
int n,Dis[1001][1001],Vis[1001][1001],number[1001],timek,k[1001][1001];
struct P
{
    int x,y;
    bool operator < (const P& ths) {return Dis[x][y]<Dis[ths.x][ths.y];}
};
queue<P> que[3];
int getfront()
{
    int c=-1;
    if(que[0].size()) c=0;
    if(que[1].size()&&(c<0||que[1].front()<que[c].front())) c=1;
    if(que[2].size()&&(c<0||que[2].front()<que[c].front())) c=2;
    return c;
}
int Fit(int x,int y)
{
    return (x>=1&&x<=n&&y>=1&&y<=n);
}
int Dir[4][2]={{1,0},{0,1},{-1,0},{0,-1}};//方向数组
int BFS()
{
    if(k[1][1]==0||k[n][n]==0) return -1;
    que[number[cinm[1][1]]].push((P){1,1});
	Dis[1][1]=k[1][1];
    while(que[0].size()+que[1].size()+que[2].size())//当三个队列的大小和非空时
	{
        int t=getfront();
	    int x=que[t].front().x;
		int y=que[t].front().y;//获取坐标
		que[t].pop();
        if(x==n&&y==n) return Dis[x][y];
        if(Vis[x][y]==1) continue;
		Vis[x][y]=1;
        for(int i=0;i<=3;i++)
		{
            int nx=x+Dir[i][0];
			int ny=y+Dir[i][1];
            if(Fit(nx,ny)&&k[nx][ny]!=0&&Dis[x][y]+k[nx][ny]<Dis[nx][ny])
			{
                Dis[nx][ny]=Dis[x][y]+k[nx][ny];
                que[number[cinm[nx][ny]]].push((P){nx,ny});
            }
        }
    }
    return -1;
}
int main()
{
    n=read();
    for(int i=1;i<=n;i++) scanf("%s",cinm[i]+1);
    memset(number,-1,sizeof(number));
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
	    {
            if(number[cinm[i][j]]<0&&cinm[i][j]!=‘0‘) number[cinm[i][j]]=timek++;//为非0元素标号
            Dis[i][j]=99999999;
            k[i][j]=cinm[i][j]-‘0‘;
        }
    printf("%d\n",BFS());
}
时间: 2024-10-05 17:24:33

【HDNOIP】HD201404最短路径的相关文章

ACM: HDU 3790 最短路径问题-Dijkstra算法

HDU 3790 最短路径问题 Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Description 给你n个点,m条无向边,每条边都有长度d和花费p,给你起点s终点t,要求输出起点到终点的最短距离及其花费,如果最短距离有多条路线,则输出花费最少的. Input 输入n,m,点的编号是1~n,然后是m行,每行4个数 a,b,d,p,表示a和b之间有一条边,且其长度为d,花费为p.最后一行是

最短路径算法专题1----弗洛伊德

由于最短路径算法我认为比较重要,所以分成几个专题来慢慢细化去磨它,不能一口气吃个胖子嘛. 首先在说算法之前,先说清楚什么叫做最短路径. 题目一般会给你一张图,然后告诉你很多地方,然后告诉你各个地方之间的路程有多远,要你求出,两点间的最短距离,注意,题目给出的两点间的距离未必是最短的,可能通过第三个点转换之后达到更短.实际其实也是这样的,有时候两个地方并没有直线的道路只有曲线的绕路. 算法的思路: 1.用二维数组列出所有的距离,达到不了的用最大距离表示,如9999999 2.循环数组上面的每一个点

postgresql+postgis+pgrouting实现最短路径查询(1)---线数据的处理和建立拓扑

准备一个线shp数据,并将其导入postgres里面,postgres安装postgis和pgrouting两个插件(方法见http://www.cnblogs.com/nidaye/p/4553522.html).线数据的字段如下:注意字段的名称,省的出现不必要的麻烦. 1.ALTER TABLE beijing_line ADD COLUMN source integer; ALTER TABLE beijing_line ADD COLUMN target integer; ALTER T

算法导论——最短路径Dijkstra算法

package org.loda.graph; import org.loda.structure.IndexMinQ; import org.loda.structure.Stack; import org.loda.util.In; /** * * @ClassName: Dijkstra * @Description: Dijkstra最短路径算法--贪心算法 * @author minjun * @date 2015年5月27日 下午4:49:27 * */ public class D

最短路径(四)—Bellman-Ford的队列优化(邻接表)

上一节我们写了Bellman-Ford算法解决负权边的问题: 邻接表存储图: n个顶点,m条边. 数组实现邻接表.对每一条边进行1-m编号.用u,v,w三个数组来记录每条边的信息,即u[i],v[i],w[i]表示第i条边是从第 u[i]号顶点到v[i]号顶点且权值为w[i]. first数组的1-n号单元格分别用来存储1-n号顶点的第一条边的编号,初始的时候因为没有边加入所有都是-1.即first[u[i]]保存顶点u[i]的第一条边的编号,next[i]存储"编号为i的边"的&qu

最短路径(Floyd法)

最短路径法: 算法的主要思想是:单独一条边的路径也不一定是最佳路径. 从任意一条单边路径开始.所有两点之间的距离是边的权的和,(如果两点之间没有边相连, 则为无穷大). 对于每一对顶点 u 和 v,看看是否存在一个顶点 w 使得从 u 到 w 再到 v 比己知的路径更短.如果是更新它. 先把所有的结果都计算出来放在数组里面,然后根据需要输出所需要的两点之间的最短路径.用了三个循环来实现 还有一个要Mark一下的是:不如一个数组s[i][j]:那可以用这个数组来存放三个数 i,j和s[i][j]:

最短路径算法——迪杰斯特拉算法(Dijkstra)

图结构中应用的最多的就是最短路径的查找了,关于最短路径查找的算法主要有两种:迪杰斯特拉算法(Dijkstra)和Floyd算法. 其中迪杰斯特拉算法(Dijkstra)实现如下: 原理就是不断寻找当前的最优解: void main() { int V[Max][Max]={0,8,32,Infinity,Infinity, 12,0,16,15,Infinity, Infinity,29,0,Infinity,13, Infinity,21,Infinity,0,7, Infinity,Infi

[BZOJ 1576] 安全路径 最短路径树 路径压缩

题意 给定一张 n 个点 m 条边的图, 保证对于任意的点 i , 从点 1 到点 i 的最短路唯一. 对于任意的点 i , 询问: 将 1 到 i 的最短路中最后一条边删去之后, 从 1 到 i 的最短路 . n <= 100000, m <= 200000 . 分析 首先跑 Dijsktra , 构建出最短路径树. 接下来考虑每条非树边 E[p] = (u, v, d) 对答案的影响, 它能且仅能影响到点 u, v 之上, LCA(u, v) 之下的点的答案. (包括 u, v, 不包括

四大最短路径算法比较

  Floyd Dijkstra Bellman-Ford 队列优化的Bellman-ford 空间复杂度 O(N²) O(M) O(M) O(M) 时间复杂度 O(N3) O((M+N)logN) O(NM) O(NM) 适用情况 稠密图,和顶点关系密切 稠密图,和顶点关系密切 稀疏图,和边关系密切 稀疏图,和边关系密切 负权 可以解决 不能解决 可以解决 可以解决 注1:N为定点数,M为边数 注2:  Floyd的编码复杂度较小,均摊到每个点上的时间复杂度并不算太高,如果是求所有点对间的最短