题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=4362
2012年多校 第7场 1003。
赛后听他们说,是用 dp+优先队列优化 过的。。。
我比赛时写的方法 应该是 贪心+二分, 勉强也可以算作 DP+二分了吧。。。 (跑了400MS,还要快一些)
时间复杂度 O( n * m * lg m )
很简单, 用两个数组 : Left 代表前一行每一个点从左往右走的最佳状态, Right 代表前一行每一个点从右往左走的最佳状态。
然后在遍历当前行的时候,用二分得到 Left 中最佳点 往右走到当前点需要的花费m1,二分得到 Right 最佳点 往左走到当前点需要的花费m2。
取m1的m2的最小值 加到当前点的 val上。
=====================================================================================
PS: 二分不太熟悉,写的头晕晕,状态也不太好,在二分上花的时间近一小时(先后改了又改,重写二分)。。。囧的一逼。。。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cmath>
#include<cstring>
#define kabs(x) ((x)<0?0-(x):(x))
using namespace std;
const __int64 inf = (__int64)0x3fffffff*0x3fffffff;
int n,m,k;
struct Node{
__int64 loc,val;
}no[60][1010];
__int64 ml[1010],mr[1010];
int cmp(Node a,Node b){
return a.loc<b.loc;
}
int main(){
int t,tt=1,i,j;
scanf("%d",&t);
while(t--){
scanf("%d%d%d",&n,&m,&k);
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
scanf("%I64d",&no[i][j].loc);
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
scanf("%I64d",&no[i][j].val);
for(i=1;i<=n;i++) // 需要按照坐标进行排序
sort(no[i]+1,no[i]+1+m,cmp);
for(j=1;j<=m;j++) // 添加 起点到第一行各点的花费
no[1][j].val+=kabs(no[1][j].loc-k);
int l,r,mid;
__int64 m1,m2;
for(i=2;i<=n;i++){
ml[1]=no[i-1][1].val;
for(j=2;j<=m;j++) // 预处理出前一行 Left 数组
ml[j]=min(no[i-1][j].val,ml[j-1]+no[i-1][j].loc-no[i-1][j-1].loc);
mr[m]=no[i-1][m].val;
for(j=m-1;j>=1;j--) // 预处理前一行 Right 数组
mr[j]=min(no[i-1][j].val,mr[j+1]+no[i-1][j+1].loc-no[i-1][j].loc);
for(j=1;j<=m;j++){ // 遍历当前行每一个点
if(no[i][j].loc>=no[i-1][1].loc){ // 二分往右走
l=1;r=m;
while(l<=r){
mid=(l+r)>>1;
if(no[i][j].loc>=no[i-1][mid].loc)
l=mid+1;
else r=mid-1;
}
m1=no[i][j].loc-no[i-1][l-1].loc+ml[l-1];
}
else m1=inf;
if(no[i][j].loc<=no[i-1][m].loc){ // 二分往左走
l=1;r=m;
while(l<=r){
mid=(l+r)>>1;
if(no[i-1][mid].loc>=no[i][j].loc)
r=mid-1;
else l=mid+1;
}
m2=no[i-1][r+1].loc-no[i][j].loc+mr[r+1];
}
else m2=inf;
// printf("m1: %I64d\n",m1);
// printf("m2: %I64d\n",m2);
no[i][j].val+=min(m1,m2); // 将最小值 添加到 当前点的val上
}
}
__int64 res=no[n][1].val;
for(j=2;j<=m;j++) // 取最后一行的最小值 输出
if(res>no[n][j].val) res=no[n][j].val;
printf("%I64d\n",res);
}
return 0;
}