「BSOJ1046」 相邻项序列 - 最短路

题目描述

对于一个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;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值