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

点此看题面

大致题意: 一张\(m\times 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;//输出答案
}
上一篇:Introduction to Linear Algebra(6) Orthogonal and Least Squares


下一篇:nm命令符号解释