http://acm.split.hdu.edu.cn/showproblem.php?pid=5492
题意:一个n*m方格,每个格子上有自己的权值,从1,1出发每次只能 从(x, y) 走向(x + 1, y) 或 (x, y +1),终点n,m,路径长度必然就是n+m-1,求(N+M−1)∑N+M−1i=1(Ai−Aavg)2
的最小值,其中Ai是路径上的第i个权值,Aavg是路径上权值平均值
把上诉公式完全平方展开,会等于(n+m-1)*(A1^2+A2^2+...)-(A1+A2+A3+...)^2
看到这个公式,很明显就是dp了,却误以为二维dp就够了,每个点选从左边走来更优还是上边走来更优,然而。。。很容易举反例
3 4
5 5 9 9
2 5 2 2
9 9 2 2
当前最优不代表全局最优!
就像到(2,2)这个点时,当前最优应该是(1,1)->(1,2) ->(2,2)这条路劲
但参考全局前几步应该选择(1,1)->(2,1)->(2,2)]
看了别人的题解发现并不难,加个维度就好了
因为每个权值都不大,所以可以加一个(A1+A2+A3+。。。)的维度,因为要上式结果最小,对于相同的sum(A),必然(n+m-1)*(A1^2+A2^2+...)越小越好
而(n+m-1)是常数,只需要记录相同sum的最小(A1^2+A2^2+...)即可
avgAavgAavg
#include<bits/stdc++.h>
#define ll long long
using namespace std;
long long dp[35][35][2006];
int a[50][50];
long long int inf=0x3f3f3f3f3f3f3f3f;
int main()
{
int tca=1,T,n,m,i,j,k;
cin>>T;
while(T--)
{
cin>>n>>m;
for(i=1;i<=n;i++)
{
for(j=1;j<=m;j++)
{
scanf("%d",&a[i][j]);
}
}
memset(dp,inf,sizeof(dp));
dp[1][1][a[1][1]]=a[1][1]*a[1][1];
for(i=1;i<=n;i++)
{
for(j=1;j<=m;j++)
{
for(k=0;k<=2000;k++)
{
if(dp[i][j][k]!=inf)
{
dp[i+1][j][k+a[i+1][j]]=min(dp[i+1][j][k+a[i+1][j]],dp[i][j][k]+a[i+1][j]*a[i+1][j]);
dp[i][j+1][k+a[i][j+1]]=min(dp[i][j+1][k+a[i][j+1]],dp[i][j][k]+a[i][j+1]*a[i][j+1]);
}
}
}
}
long long ans=inf;
for(i=0;i<=2000;i++)
{
if(dp[n][m][i]!=inf)
ans=min(ans,dp[n][m][i]*(n+m-1)-i*i);
}
printf("Case #%d: %lld\n",tca++,ans);
}
return 0;
}