Time Limit: 5000/2000 MS (Java/Others) Memory Limit: 62768/32768 K (Java/Others)
Total Submission(s): 677 Accepted Submission(s): 226
题意:
要在N*M(n<=100.m<=5000)的矩形区域的每行的一个位置建灯塔。而在第i行的j列建塔要花费时间ti[i][j].建塔还必须满足一个条件。
如果本行在j列建塔。下行在k列建塔。那么必须满足|j-k|<=f[i][j]+f[i+1][k]。
f[i][j]由题目给出。
问每行建完塔花费的最小时间。
思路:
很容易想到一个动规方程。dp[i][j]=min(dp[i][j],dp[i-1][k]+ti[i][j])。dp[i][j]代表前面i-1行建好塔。第i行在j列建塔的最小花费。
可问题又来了。这个k怎么确定。。。。。
如果暴力枚举的话时间复杂度为O(n*m*m)。目测最好n,m取最大的时候大于9秒吧。这还是单组数据。。。
所以不得不找其它办法优化下。
这个估计就要点思维了。我也是看了别人的转化才恍然大悟的。
我们去掉绝对值可以得到两个方程。
j-f[i][j]<=k+f[i+1][k]。 j>=k。
k-f[i+1][k]<=j+f[i][j]。 j<k。
可以发现k的取值范围即为区间[j-f[i][j],j+f[i][j]]和[k-f[i+1][k],k+f[i+1][k]]相交的部分。
所以思路就清晰了。要求dp[i][j]只需要知道上一行和[j-f[i][j],j+f[i][j]]相交部分的最小值就可以了。
而这个值可以用线段树维护。时间复杂度降到了。O(n*m*log(m))这下就没问题了。
详细见代码:
#include<algorithm> #include<iostream> #include<sstream> #include<string.h> #include<stdio.h> #include<math.h> #include<vector> #include<string> #include<queue> #include<map> using namespace std; const int INF=0x3f3f3f3f; const int maxn=150; const int maxm=5010; int ti[maxn][maxm],f[maxn][maxm],minv[maxm<<2],lazy[maxm<<2]; int dp[maxm],n,m; void btree(int L,int R,int k) { int ls,rs,mid; minv[k]=lazy[k]=INF; if(L==R) return ; ls=k<<1; rs=ls|1; mid=(L+R)>>1; btree(L,mid,ls); btree(mid+1,R,rs); } void pushdown(int k,int ls,int rs) { minv[ls]=min(minv[ls],lazy[k]); minv[rs]=min(minv[rs],lazy[k]); lazy[ls]=min(lazy[ls],lazy[k]); lazy[rs]=min(lazy[rs],lazy[k]); lazy[k]=INF; } void update(int L,int R,int l,int r,int k,int v) { int ls,rs,mid; if(L==l&&R==r) { minv[k]=min(minv[k],v); lazy[k]=min(lazy[k],v); return; } ls=k<<1; rs=ls|1; mid=(L+R)>>1; if(lazy[k]!=INF) pushdown(k,ls,rs); if(l>mid) update(mid+1,R,l,r,rs,v); else if(r<=mid) update(L,mid,l,r,ls,v); else { update(L,mid,l,mid,ls,v); update(mid+1,R,mid+1,r,rs,v); } minv[k]=min(minv[ls],minv[rs]); } int qu(int L,int R,int l,int r,int k) { int ls,rs,mid; if(L==l&&R==r) return minv[k]; ls=k<<1; rs=ls|1; mid=(L+R)>>1; if(lazy[k]!=INF) pushdown(k,ls,rs); if(l>mid) return qu(mid+1,R,l,r,rs); else if(r<=mid) return qu(L,mid,l,r,ls); else return min(qu(L,mid,l,mid,ls),qu(mid+1,R,mid+1,r,rs)); } int main() { int i,j,l,r,ans; while(scanf("%d%d",&n,&m),n||m) { for(i=1;i<=n;i++) for(j=1;j<=m;j++) scanf("%d",&ti[i][j]); for(i=1;i<=n;i++) for(j=1;j<=m;j++) scanf("%d",&f[i][j]); for(i=1;i<=m;i++) dp[i]=ti[1][i]; for(i=2;i<=n;i++) { btree(1,m,1); for(j=1;j<=m;j++) { l=max(j-f[i-1][j],1); r=min(j+f[i-1][j],m); update(1,m,l,r,1,dp[j]); } for(j=1;j<=m;j++) { l=max(j-f[i][j],1); r=min(j+f[i][j],m); dp[j]=qu(1,m,l,r,1)+ti[i][j]; } } ans=INF; for(i=1;i<=m;i++) ans=min(ans,dp[i]); printf("%d\n",ans); } return 0; }