2019中山纪念中学夏令营-Day20[JZOJ] T1旅游详解
2019中山纪念中学夏令营-Day20[JZOJ]
提高组B组 Team_B组
T1 旅游 Time Limits: 2000 ms Memory Limits: 262144 KB
官方正解:
用f[x][y]表示游览到(x,y)时可能的最长时间,将曼哈顿距离中的绝对值展开后,转移可以表示为四个子矩形中求max的问题,可以使用二维树状数组维护
继续观察发现因为题目要求总长度尽可能长,因此每个点只会在正确的子矩形中贡献最大的答案,因此不需要使用二维树状数组,只需按符号考虑四种情况即可
听说好像要用四边形不等式(反正我也不懂,就瞎搞吧)
我的思路:
先用结构体存矩阵,使其压成一维,然后开始DP乱搞
我们显然可以发现,最优解一定是从上一个观赏度和当前观赏度之差绝对值最小的地方转换过来的
因此可以给DP剪掉一些不可能最优的转移,因为每个点i的bi(即观赏时间)是一定的,我们可以把它从转移式中提出来。
状态转移方程:
其中:k为上一阶段的观赏度的点排序后的编号(用来剪不必要的转移用的),如果不理解的话可以将j=0,但是会慢很多。
代码实现:
1 #include <cstdio> 2 #include <algorithm> 3 #define rr register 4 #define int long long 5 using std::sort; 6 int n,m,f[1005*1005],mark1,mark2,maxn,h[1005][1005]; 7 struct Node{ 8 int x,y,a,b; 9 }node[1005*1005]; 10 bool cmp(Node k,Node l) 11 { 12 return k.a<l.a; 13 } 14 int max(int k,int l) 15 { 16 if(k>l) 17 return k; 18 return l; 19 } 20 int abs(int k) 21 { 22 if(k<0) 23 return -k; 24 return k; 25 } 26 signed main() 27 { 28 scanf("%lld %lld",&n,&m); 29 int tot=1; 30 for(rr int i=1;i<=n;i++) 31 for(rr int j=1;j<=m;j++) 32 { 33 scanf("%lld",&h[i][j]); 34 } 35 tot=1; 36 for(rr int i=1;i<=n;i++) 37 for(rr int j=1;j<=m;j++) 38 { 39 int tmp; 40 scanf("%lld",&tmp); 41 if(tmp==0 && h[i][j]==0) 42 continue; 43 node[tot].a=h[i][j]; 44 node[tot].b=tmp; 45 node[tot].x=i; 46 node[tot].y=j; 47 tot++; 48 } 49 tot--; 50 sort(node+1,node+tot+1,cmp); 51 for(rr int i=1;i<=tot;i++) 52 { 53 if(node[i].a>node[i-1].a) 54 { 55 mark2=mark1; 56 mark1=i-1; 57 } 58 if(node[i].a==0 && node[i].b==0) 59 continue; 60 61 for(rr int j=max(mark2,1);j<i;j++) 62 { 63 if(node[i].a>node[j].a) 64 { 65 if(f[i] < f[j]+abs(node[i].x-node[j].x)+abs(node[i].y-node[j].y)) 66 f[i] = f[j]+abs(node[i].x-node[j].x)+abs(node[i].y-node[j].y); 67 } 68 } 69 f[i]+=node[i].b; 70 maxn=max(maxn,f[i]); 71 } 72 73 printf("%lld",maxn); 74 }