题目描述
对于一个n*n (n<=100) 的正整数矩阵M,存在从M[A1,B1]开始到M[A2,B2]结束的相邻项序列,两个项M[x1,y1]和M[x2,y2]相邻是指满足条件:|x1-x2|+|y1-y2|=1且1<=x1,y1,x2,y2<=n
你的任务:从文件中输入矩阵M,A1,B1和A2,B2的值。求从M[A1,B1]开始到M[A2,B2]结束的相邻项序列,使得相邻项之差的绝对值之和为最小。
分析
乍一看,感觉像是一个Dp题,然而Dp是有后效性的。再一读题,发现n的范围比较小,所以对于矩阵内每一个元素,向它的相邻元素建边,边权为两个的差的绝对值,然后就可以从起点跑一遍最短路即可。
代码
#include <cstdio>
#include <queue>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=100*100+5;
struct Edge {
int to,next,weight;
}e[N*10];
int h[N],cnt;
int n;
int g[101][101];
int nxt[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
int d[N],vis[N];
void add(int x,int y,int z) {
e[++cnt]=(Edge){y,h[x],z};
h[x]=cnt;
}
int getnum(int i,int j) {//坐标转编号
return n*(i-1)+j;
}
void spfa(int v0) {
memset(vis,0,sizeof vis);
memset(d,0x3f,sizeof d);
d[v0]=0;
vis[v0]=1;
queue<int> q;
q.push(v0);
while (!q.empty()) {
int t=q.front();
q.pop();
vis[t]=0;
for (int i=h[t];i;i=e[i].next) {
int y=e[i].to;
if (d[y]>d[t]+e[i].weight) {
d[y]=d[t]+e[i].weight;
if (!vis[y]) {
vis[y]=1;
q.push(y);
}
}
}
}
}
int main() {
scanf("%d",&n);
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++) scanf("%d",&g[i][j]);
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
for (int k=0;k<4;k++) {
int tx=i+nxt[k][0],ty=j+nxt[k][1];
if (tx<=0||tx>n||ty<=0||ty>n) continue;
add(getnum(i,j),getnum(tx,ty),abs(g[i][j]-g[tx][ty]));
//只建单向边,因为后边的节点会再一次循环到这个节点
}
int sx,sy,gx,gy;
int snum,gnum;
scanf("%d%d%d%d",&sx,&sy,&gx,&gy);
snum=getnum(sx,sy);
gnum=getnum(gx,gy);
spfa(snum);
printf("%d",d[gnum]);
return 0;
}