易得此题DP方程: dp[i][j]=min{dp[i-1][k]+abs(x[i-1][k]-x[i][j])}+cost[i][j]; //1<=i<=n,1<=j,k<=m;
当x[i-1][k]<=x[i][j]时,dp[i][j]=min{dp[i-1][k]-x[i-1[k]}+x[i][j]+cost[i][j]; (1)
当x[i-1][k]>x[i][j]时,dp[i][j]=min{dp[i-1][k]+x[i-1][k]}-x[i][j]+cost[i][j]; (2)
(1)和(2)式中的min{}部分均与j无关,单调性明显。
故可对点[i][j]按坐标排序,对于每层,从左往右(1),从右往左(2)两次,在坐标关系满足x[i-1][k]<=x[i][j](x[i-1][k]>x[i][j])中找到dp[i-1][k]-x[i-1][k](dp[i-1][k]+x[i-1][k])最小的点再做转移即可。
AC代码如下:
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> using namespace std; const int NN=1010; const int INF=0x3fffffff; int dp[2][NN]; struct node{ int x,w; bool operator <(const node a)const{ return x<a.x; } }a[NN][NN]; int main() { int T,c,n,m,k,v,x0; scanf("%d",&T); while (T--) { scanf("%d%d%d",&n,&m,&x0); for (int i=1; i<=n; i++) for (int j=1; j<=m; j++) scanf("%d",&a[i][j].x); for (int i=1; i<=n; i++) { for (int j=1; j<=m; j++) scanf("%d",&a[i][j].w); sort(a[i]+1,a[i]+m+1); a[i][m+1].x=INF; a[i][0].x=-1; } //dp for (int j=1; j<=m; j++) dp[1][j]=abs(a[1][j].x-x0)+a[1][j].w; c=1; for (int i=2; i<=n; i++) { c=i%2; v=0; k=1; for (int j=1; j<=m; j++) { dp[c][j]=INF; while (a[i-1][k].x<=a[i][j].x) { if (!v || dp[c^1][v]-a[i-1][v].x>dp[c^1][k]-a[i-1][k].x) v=k; k++; } if (v) dp[c][j]=min(dp[c][j],dp[c^1][v]-a[i-1][v].x+a[i][j].x+a[i][j].w); } v=0; k=m; for (int j=m; j; j--) { while (a[i-1][k].x>=a[i][j].x) { if (!v || dp[c^1][v]+a[i-1][v].x>dp[c^1][k]+a[i-1][k].x) v=k; k--; } if (v) dp[c][j]=min(dp[c][j],dp[c^1][v]+a[i-1][v].x-a[i][j].x+a[i][j].w); } } int ans=INF; for (int j=1; j<=m; j++) ans=min(ans,dp[c][j]); printf("%d\n",ans); } return 0; }