VIJOS-P1474 雷曼兔(csapc)
题目传送门
描述
这次,OI山成为了雷曼兔那无尽的冒险传说的新舞台!传说OI山中埋藏着巨大的宝藏,伴随着这个传说的是一个迷题:最瑰丽的舞者将达至精灵世界的彼岸……
经过仔细推敲,雷曼兔发现这是一个提示宝藏埋藏位置的谜语,在该谜语中指出了一个特定的路径,只有经过了该路径宝藏才会出现,具体情况如下:
- OI山的地势图可以看作一个 \(N \times N\) 的数字矩阵,由 \(1,2 \dots N \dots N^2\) 的数字组成(每个数字出现且仅出现一次),这些数字表示每个地点的地势高低。
- 雷曼兔的出发点在最高的山顶处,并且每次雷曼兔可以从其当前所在的位置跳跃到任何一个比当前地点高度低的位置,假设雷曼兔该次跳跃从坐标 \((x_1,y_1)\) 跳到了坐标 \((x_2,y_2)\),则这次跳跃的华丽度定义为 \(v=(\vert x_1-x_2 \vert + \vert y_1-y_2 \vert)^2\)。开启宝藏秘密的路径就是从山顶不断跳跃直到山底(高度最低点)的华丽度总和最高的路径。
- 现在我们想要知道的是这个最高的华丽度总和是多少。
格式
输入格式
第一行包括一个整数 \(n~(n<=50)\) 表示地图的长宽。
接下来 \(n\) 行每行 \(n\) 个数表示每个地点的高度。
输出格式
输出包括一个整数 \(ans\),表示从山顶到山底最高华丽度总和。
样例
输入
2
3 2
1 4
输出
9
提示
最优路径为 \(4 \to 3 \to 2 \to 1\),得分为 \(4+1+4=9\)。
题解
分析
因为计算华丽度的时候需要用到坐标,所以要把高度和坐标同时存进去。可用二维数组,也可用结构体。
\(dp[i]\) 表示从高度为 \(i\) 的点跳跃到山脚的华丽度的最大值。
转移方程就是 \({dp[i] = max(dp[i],~getdis(i,j)+dp[j])}\)
这时就出现了一个问题:\(dp\) 数组的 初值 问题。
显然,由 \(dp\) 数组的定义,\(dp[i]\) 的初值就应该是直接从 \(i\) 点跳跃到山脚的华丽度。
然后我们在松弛的过程中,不难发现这题的转移方程其实跟 \(Floyd\) 挺像的,都是去找断点更新答案(只不过 \(Floyd\) 是求解最小值,此题是求解最大值)。
代码
#include <cstdio>
#include <cmath>
#include <algorithm>
#define N 2510
using namespace std;
int n, dp[N], h; //dp[i]表示从高度为i的点跳到山底的最大华丽度
struct Map
{
int x, y;
}m[N];
int getdis(int x, int y)
{
return (abs(m[x].x-m[y].x) + abs(m[x].y-m[y].y)) * (abs(m[x].x-m[y].x) + abs(m[x].y-m[y].y));
}
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
{
scanf("%d", &h);
m[h].x = i, m[h].y = j;
}
int nn = n*n;
for(int i = 2; i <= nn; i++)
{
dp[i] = getdis(i, 1);
for(int j = 1; j < i; j++) dp[i] = max(dp[i], getdis(i, j)+dp[j]);
}
printf("%d", dp[nn]);
return 0;
}