题意:给定一个n*m的矩阵,里面的元素是1~p中的,并且元素p只有一个,1~p-1的每个元素都至少1个。元素x表示钥匙x,有x的钥匙才能拿到x+1的钥匙,1是没有被锁起来的。求最后打开p那个宝箱最少要走多少步。
分析:如果我们暴力直接用元素i去更新所有的i+1那么最差复杂度是n^2*m^2,即只有1,2并且p=3的时候。多个点更新多个点。好像不好处理,CF的官方题解好像是二娃线段树,我没有细想。我的做法是n*m*(n+m)的,我们考虑每一个元素都会被更新和用来更新其他元素。我们考虑当前用元素i更新i+1,对于x我们用O(m)的时间去更新和它同一行的位置最少要走多少步才能到,当我们处理完所有i元素的行后我们用每一个i+1的元素去用O(n)去找它所在的列中最优的距离。这样我们就可以用O(n*m*(n+m))的时间求得到达p的最小路径长度了。
代码:
#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<bitset>
#include<math.h>
#include<cstdio>
#include<vector>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
const int N=100010;
const int MAX=2000000000;
const int mod=100000000;
const int MOD1=1000000007;
const int MOD2=1000000009;
const double EPS=0.00000001;
typedef long long ll;
const ll MOD=1000000007;
const int INF=1000000010;
const double pi=acos(-1.0);
typedef double db;
typedef unsigned long long ull;
struct node {
int x,y,z;
node() {}
node(int _x,int _y,int _z):x(_x),y(_y),z(_z) {}
};
int f[310][310];
vector<node>q[90010];
int main()
{
int a,i,j,k,n,m,p;
scanf("%d%d%d", &n, &m, &p);
for (i=1;i<=n;i++)
for (j=1;j<=m;j++) {
scanf("%d", &a);q[a].push_back(node(i,j,MAX));
}
for (i=0;i<q[1].size();i++) q[1][i].z=abs(q[1][i].x-1)+abs(q[1][i].y-1);
for (i=1;i<=n;i++)
for (j=1;j<=m;j++) f[i][j]=MAX;
for (i=1;i<p;i++) {
for (j=0;j<q[i].size();j++)
for (k=1;k<=m;k++) f[q[i][j].x][k]=min(f[q[i][j].x][k],q[i][j].z+abs(k-q[i][j].y));
for (j=0;j<q[i+1].size();j++)
for (k=1;k<=n;k++) q[i+1][j].z=min(q[i+1][j].z,f[k][q[i+1][j].y]+abs(k-q[i+1][j].x));
for (j=0;j<q[i].size();j++)
for (k=1;k<=m;k++) f[q[i][j].x][k]=MAX;
}
printf("%d\n", q[p][0].z);
return 0;
}