Description
nodgd在旅游。现在,nodgd要从城市的西北角走到东南角去。这个城市的道路并不平坦,nodgd希望找出一条相对比较好走的路。
nodgd事先已经得到了这个城市的地图。地图上这个城市是一个n*m的矩形,nodgd现在站在坐标为(1,1)的位置,需要到达坐标为(n,m)的位置。这张地图上用非负整数标记了每个整数坐标点的海拔,坐标为(x,y)的位置的海拔是ℎ(x,y)。nodgd希望找出一条路线,路线中任意时刻都在向正东或向正南走,而且只在整数坐标点的地方转弯,使得路上经过的n+m−1个整数坐标点的海拔的方差最小。然而万能的nodgd当然知道该怎么走,也当然知道方差最小是多少,只是想顺便考考你。
在本题中为了方便,你只需要求出(n+m−1)^2×σ^2的最小值即可,众所周知这是个整数。
Input
第一行输入两个整数n,m,表示城市的大小。
接下来n行,每行m个数,其中第x行第y个数就是ℎ(x,y)。
Output
输出一行一个整数,表示(n+m−1)^2×σ^2的最小值。
Sample Input
2 2
1 2
3 4
Sample Output
14
Data Constraint
对于30%的数据,1≤n,m≤10;
对于50%的数据,1≤n,m≤20;
对于100%的数据,1≤n,m≤50,0≤ℎ(x,y)≤50。
Hint
样例解释:有两条路1-2-4和1-3-4,方差都等于14/9,所以方差最小值是14/9,输出14。
Solution
首先化简式子,设
k=n+m−1S=X1+X2+……+Xk则有:
x=Sk
化简:
σ2=(X2k+X22+……+X2k)−S2kk所以就得到式子:
k2σ2=k(X2k+X22+……+X2k)−S2观察数据范围,搜索似乎不太可行,于是我们考虑DP。
设 F[i][j][S] 表示走到 (i,j) ,状态为 S 的最优值。
发现设最优值不好转移,根据单调性可知可以改成最优值下的平方和,最后统计一遍答案即可。
往下走则有:
F[i+1][j][S+a[i+1][j]]=Min{F[i][j][S]+a[i+1][j]2} 往右走同理,由于 h 值的限制,时间复杂度就是 O(N4) ,可以接受。
总结:
看到式子就想到化简;
看到方差就想到 平方和 与 和的平方 ;
平均数 可以用 总和 除以 总数 代替。
Code
#include<cstdio>
#include<cstring>
#define work(x,y) x=min(x,y)
using namespace std;
const int N=52,inf=1e9;
int n,m,num,mx,ans=inf;
int a[N][N],b[N][N],f[N][N][N*N<<1];
inline int read()
{
int X=0,w=1; char ch=0;
while(ch<'0' || ch>'9'){if(ch=='-') w=-1;ch=getchar();}
while(ch>='0' && ch<='9') X=(X<<3)+(X<<1)+ch-'0',ch=getchar();
return X*w;
}
inline int max(int x,int y)
{
return x>y?x:y;
}
inline int min(int x,int y)
{
return x<y?x:y;
}
int main()
{
n=read(),m=read(),num=n+m-1;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
mx=max(mx,a[i][j]=read());
b[i][j]=a[i][j]*a[i][j];
}
memset(f,60,sizeof(f));
f[1][1][a[1][1]]=b[1][1];
mx*=num;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
for(int k=0;k<=mx;k++)
{
if(i<n && k+a[i+1][j]<=mx) work(f[i+1][j][k+a[i+1][j]],f[i][j][k]+b[i+1][j]);
if(j<m && k+a[i][j+1]<=mx) work(f[i][j+1][k+a[i][j+1]],f[i][j][k]+b[i][j+1]);
}
for(int k=0;k<=mx;k++)
if(f[n][m][k]<inf) work(ans,num*f[n][m][k]-k*k);
printf("%d",ans);
return 0;
}