大致题意: 一张(m imes m)的图上有(n)个点,每个点有一个收益。每次只能从一个点到右下方的另一个点,代价是两点间距离的平方。求从((1,1))到((n,m))收益减代价的最大值。
暴力
对于每一个关键点,显然可以枚举所有在它左上方的点作为到达它的前一个点。
若之前出现过位于同一列的两点(更多点也是同理的),设它们分别为((x,t),(y,t)(x<y)),且当前点为((i,j))。然后考虑以下两种方案的代价:
- 从((x,t))直接到((i,j)):((x-i)^2+(t-j)^2)。
- 从((x,t))到((y,t))再到((i,j)):((x-y)^2+(y-i)^2+(t-j)^2)。(其实此处还有(a_{y,t})的收益,实际代价要更小)
换元,令(x-y=p,y-i=q),同时约去两式中的((t-j)^2),显然有:
[(x-i)^2=(p+q)^2=p^2+q^2+2pq>p^2+q^2
]
也就是说,对于同一列从最下方的点转移必然是最优的。
于是我们就得到了一个(O(nm))的暴力(DP),这个相信大家都会写的吧。。。
而且(O(nm))是跑不满的,实测跑得飞快。
代码
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define M 1000
#define Gmax(x,y) (x<(y)&&(x=(y)))
#define Sqr(x) ((x)*(x))
using namespace std;
int n,m,a[M+5][M+5],f[M+5][M+5],g[M+5],w[M+5];
class FastIO
{
private:
#define FS 100000
#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
#define D isdigit(c=tc())
char c,*A,*B,FI[FS];
public:
I FastIO() {A=B=FI;}
Tp I void read(Ty& x) {x=0;W(!D);W(x=(x<<3)+(x<<1)+(c&15),D);}
}F;
int main()
{
RI i,j,k,x,y;for(F.read(n),F.read(m),i=1;i<=n;++i) F.read(x),F.read(y),F.read(a[x][y]);//读入点
for(i=1;i<=m;++i) g[i]=-1e9;for(i=1;i<=m;++i) for(j=1;j<=m;++j) if(a[i][j])//只处理关键点
{
for(f[i][j]=i==1&&j==1?0:-1e9,k=1;k<=j;++k) Gmax(f[i][j],g[k]-Sqr(i-w[k])-Sqr(j-k));//枚举之前每一列DP
g[j]=(f[i][j]+=a[i][j]),w[j]=i;//更新当前点所在列信息
}return printf("%d",f[m][m]),0;//输出答案
}