CodeForces 677D Vanya and Treasure
$dp$,树状数组。
很明显这是一个$DAG$上的$dp$,由于边太多,暴力$dp$会超时,需要优化。
例如计算$dp[x][y]$,可以将区域分成四块,$dp[x][y]$取四块中的最小值,每一块用一个二维树状数组维护最小值即可。
每次扩展一层需要一个新的树状数组,因为每次初始化树状数组会超时,所以可以额外开一个数组记录一下每一个点是第几次更新的。
#pragma comment(linker, "/STACK:1024000000,1024000000") #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<vector> #include<map> #include<set> #include<queue> #include<stack> #include<iostream> using namespace std; typedef long long LL; const double pi=acos(-1.0),eps=1e-6; void File() { freopen("D:\\in.txt","r",stdin); freopen("D:\\out.txt","w",stdout); } template <class T> inline void read(T &x) { char c=getchar(); x=0; while(!isdigit(c)) c=getchar(); while(isdigit(c)) {x=x*10+c-'0'; c=getchar();} } const int INF=0x7FFFFFFF; const int maxn=310; int n,m,p,a[maxn][maxn],c[4][maxn][maxn],d[4][maxn][maxn],dp[maxn][maxn]; vector<int>v[maxn*maxn]; int lowbit(int x){return x&(-x);} int get(int op,int h,int x,int y) { int res=INF; for(int i=x;i>0;i=i-lowbit(i)) for(int j=y;j>0;j=j-lowbit(j)) if(d[op][i][j]==h) res=min(res,c[op][i][j]); return res; } void update(int op,int h,int x,int y,int v) { for(int i=x;i<=n;i=i+lowbit(i)) for(int j=y;j<=m;j=j+lowbit(j)) { if(d[op][i][j]!=h) c[op][i][j]=INF; d[op][i][j]=h; c[op][i][j]=min(c[op][i][j],v); } } int main() { scanf("%d%d%d",&n,&m,&p); for(int i=0;i<n;i++) for(int j=0;j<m;j++) { scanf("%d",&a[i][j]); v[a[i][j]].push_back(i*m+j); } for(int k=0;k<4;k++) for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) c[k][i][j]=INF,d[k][i][k]=-100; for(int i=0;i<v[1].size();i++) dp[v[1][i]/m+1][(v[1][i]%m)+1]=v[1][i]/m+v[1][i]%m; for(int i=2;i<=p;i++) { for(int j=0;j<v[i-1].size();j++) { int r=v[i-1][j]/m,c=v[i-1][j]%m; r++; c++; update(0,i-1,r,c,dp[r][c]-r-c); update(1,i-1,r,m-c+1,dp[r][c]-r+c); update(2,i-1,n-r+1,c,dp[r][c]+r-c); update(3,i-1,n-r+1,m-c+1,dp[r][c]+r+c); } for(int j=0;j<v[i].size();j++) { int r=v[i][j]/m,c=v[i][j]%m; r++; c++; dp[r][c]=INF; if(get(0,i-1,r,c)!=INF) dp[r][c]=min(dp[r][c],get(0,i-1,r,c)+r+c); if(get(1,i-1,r,m-c+1)!=INF) dp[r][c]=min(dp[r][c],get(1,i-1,r,m-c+1)+r-c); if(get(2,i-1,n-r+1,c)!=INF) dp[r][c]=min(dp[r][c],get(2,i-1,n-r+1,c)-r+c); if(get(3,i-1,n-r+1,m-c+1)!=INF) dp[r][c]=min(dp[r][c],get(3,i-1,n-r+1,m-c+1)-r-c); } } int ans=0; for(int i=0;i<n;i++) for(int j=0;j<m;j++) if(a[i][j]==p) ans=dp[i+1][j+1]; printf("%d\n",ans); return 0; }