题目
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5492
题目来源:2015合肥网络赛第二题。
简要题意: N×M 的区域,一个人只能往右往下走,每个格子有个权值,要走到 g[N][M]
设路径的权值序列 A1⋯AN+M−1 ,求 (N+M−1)∑i=1N+M−1(Ai−Aavg)2 最小值数据范围: T⩽50;1⩽N,M⩽30;Ai⩽30
题解
设 n=N+M−1;sum=∑i=1nAi=nAavg
首先需要化简公式:
n∑i=1n(Ai−Aavg)2=n∑i=1n(A2i−2AiAavg+A2avg)=−2nAavgsum+n2A2avg+n∑i=1nA2i=n∑i=1nA2i−sum2
由于 N,M,Ai 很小,我们可以确定 sum<1800于是我们可以开三维数组 dp[i][j][k] 表示走到 g[i][j],sum=k 的最优方案。
定义 p=k−g[i][j] 即前面一步的 sum ,可得一下转移方程:
dp[i][j][k]=⎧⎩⎨⎪⎪min(dp[i−1][j][p],dp[i][j−1][p])+ng[i][j]2+p2−k2 k⩾g[i][j]∞ else最终结果就是 mini=01800dp[N][M][i]
实现
实现的时候注意初始化,应该只能初始化 dp[1][0][0],dp[0][1][0] 为 0 ,其他为
∞ 原因是只有 g[1][1] 能够作为入口,我的队友在赛场上就发生了这样的错误。
代码
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <stack>
#include <queue>
#include <string>
#include <vector>
#include <set>
#include <map>
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define sz(x) ((int)(x).size())
#define fi first
#define se second
using namespace std;
typedef long long LL;
typedef vector<int> VI;
typedef pair<int,int> PII;
LL powmod(LL a,LL b, LL MOD) {LL res=1;a%=MOD;for(;b;b>>=1){if(b&1)res=res*a%MOD;a=a*a%MOD;}return res;}
// head
const int INF = 0x3f3f3f3f;
const int N = 30+5;
const int M = 2000+5;
int dp[N][N][M];
int a[N][N];
inline int sq(int x) {
return x*x;
}
int main()
{
int t, n, m, cas = 1;
scanf("%d", &t);
while (t--) {
memset(dp, INF, sizeof dp);
dp[0][1][0] = dp[1][0][0] = 0;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
scanf("%d", a[i]+j);
}
}
int step = n+m-1, p, add;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
for (int k = a[i][j]; k < M; k++) {
p = k-a[i][j];
add = step*sq(a[i][j])+sq(p)-sq(k);
dp[i][j][k] = min(dp[i][j][k], min(dp[i-1][j][p], dp[i][j-1][p])+add);
}
}
}
int ans = INF;
for (int i = 0; i < M; i++) {
ans = min(ans, dp[n][m][i]);
}
printf("Case #%d: %d\n", cas++, ans);
}
return 0;
}