HDU 4362 - Dragon Ball

 

 

题目地址: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;
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值