【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()); }
你——悟到了么?