易得此題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;
}