【BZOJ1560】[JSOI2009] 火星藏宝图(暴力DP)

点此看题面

大致题意: 一张(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;//输出答案
}
原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ1560.html