P4056 [JSOI2009] 火星藏宝图题解
题目背景
JSOI2009第三轮二试
题目描述
在火星游玩多日,jyy 偶然地发现了一张藏宝图。根据藏宝图上说法,宝藏被埋藏在一个巨大的湖里的 N 个岛上 (2≤N≤2×105)。为了方便描述,地图把整个湖划分成 M 行 M 列 (1≤M≤1000),共 M×M 个小块,并把所有岛按照 1...N 编了号。第 i 个岛位于第 Xi 行 Yi 列 (设其坐标为 (Xi,Yi)的格子 ( Xi,Yi 均为整数,并且满足 1<=Xi,Yi<=M ),岛上藏有价值财富 Vi(1≤Vi≤10,000)。湖的左上角 (1,1) 和右下角 (M,M) 都有岛,有桥将它们与陆地相连。
jyy 没费多大劲,就找到了那个湖,同时哭笑不得地发现,所谓的财富,是各个岛上出产的珍稀水果。jyy 在左上角的岛的岸边找到了一条小木船,他可以划船到其他岛上去。划船是要消耗体力的,具体地说,等于两岛 Euclidean 距离的平方(即,从 (X1,Y1) 划船到 (X2,Y2) 所耗费的体力为 (X1−X2)2+(Y1−Y2)2 个单位)。jyy 可以吃水果来恢复体力,吃掉 1 单位价值的水果能恢复 1 单位体力。
现在 jyy 打算从 (1,1) 旅行到 (M,M),沿途收集珍稀水果。按藏宝图上的提示,jyy 离开一个岛后,就只能去该岛右下方的区域(正下和正右方向也是允许的),否则会遭遇水怪。jyy 可以在旅行途中饿一段时间,即体力为负。但抵达终点后,只要身边有足够多的水果,他就会通过吃水果将体力恢复到旅行前的水平。
jyy想知道,经过一次旅行,他最多能得到多少收益,即 jyy 收集到的水果总价值- jyy 在旅途中花的总体力 。(如果吃完所有水果他还饿着,收益就是负数,具体的例子见样例)
输入格式
第 1 行:两个整数 N,M。第 2...N+1 行:每行 3 个整数,第 i+1 行的 3 个整数分别为 Xi,Yi,Vi。每个岛的坐标不同。保证存在坐标 (1,1) 和 (M,M) 的岛。
输出格式
第 1 行:输出一个整数,表示最大收益。
输入输出样例
输入 #1复制
4 10 1 1 20 10 10 10 3 5 60 5 3 30
输出 #1复制
-4
说明/提示
样例解释
20+60+10−((3−1)2+(5−1)2)−((10−3)2+(10−5)2)=−4
数据范围
对 20% 的数据 M≤200,且 N≤2×103。
对 50% 的数据 M≤200,且 N≤2×104。
对 100% 的数据 M≤1000,且 N≤2×105。
思路
首先,排序,写出朴素DP。
代码
#include<bits/stdc++.h>
using namespace std;
long long n,m,f[200005];
struct one{
long long x,y,v;
}a[200005];
bool cmp(one a1,one b1){
if(a1.x!=b1.x){
return a1.x<b1.x;
}
else{
return a1.y<b1.y;
}
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i].x>>a[i].y>>a[i].v;
}
sort(a+1,a+n+1,cmp);
memset(f,-62,sizeof(f));
f[1]=a[1].v;
for(int i=2;i<=n;i++){
for(int j=1;j<=i-1;j++){
if(a[j].x<=a[i].x&&a[j].y<=a[i].y){
f[i]=max(f[i],f[j]-(a[i].x-a[j].x)*(a[i].x-a[j].x)-(a[i].y-a[j].y)*(a[i].y-a[j].y)+a[i].v);
}
}
}
cout<<f[n]<<endl;
return 0;
}
然后,再进行优化:
代码见下
#include<bits/stdc++.h>
using namespace std;
long long n,m,f[200005];
struct one{
long long x,y,v;
}a[200005];
bool cmp(one a1,one b1){
if(a1.x!=b1.x){
return a1.x<b1.x;
}
else{
return a1.y<b1.y;
}
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i].x>>a[i].y>>a[i].v;
}
sort(a+1,a+n+1,cmp);
memset(f,-62,sizeof(f));
f[1]=a[1].v;
for(int i=2;i<=n;i++){
if(n<=20004){
for(int j=1;j<=i-1;j++){
if(a[j].x<=a[i].x&&a[j].y<=a[i].y){
f[i]=max(f[i],f[j]-(a[i].x-a[j].x)*(a[i].x-a[j].x)-(a[i].y-a[j].y)*(a[i].y-a[j].y)+a[i].v);
}
}
}
else{
for(int j=max(1,i-1-3000);j<=i-1;j++){
if(a[j].x<=a[i].x&&a[j].y<=a[i].y){
f[i]=max(f[i],f[j]-(a[i].x-a[j].x)*(a[i].x-a[j].x)-(a[i].y-a[j].y)*(a[i].y-a[j].y)+a[i].v);
}
}
}
}
cout<<f[n]<<endl;
return 0;
}

浙公网安备 33010602011771号