题意:
给出每一个时刻地鼠出现的所有位置,问如果威威猫每一个时刻都可以打到一个地鼠的话(位置的变化会产生cost,在这道题中就是位置之间的相对距离),这样打下来最终的最小cost是多少。也可以转化成数塔问题,不过数塔问题这道题是求途径的cost最小值。
状态转移方程:
// dp[i][j]表示走到第i层(在题目中对应第i个时刻)、第j个位置所得到的最小cost
// 下面式子的前提是has[i][j] == true && has[i][k] == true
dp[i][j] = min(dp[i][j], dp[i-1][k] + abs(j-k));
需要注意的是,因为最终要求的是累加的最小cost,但是一开始的初始化工作是把dp所有位置设成最大值,所以如果没有一个“特殊位置的处理”的话,最终结果始终会很大。而这个所谓“特殊位置的处理”,就是把顶层的地鼠位置的代价设为0,因为题意也说了“打第一只地鼠无能量消耗”。
代码(15ms, 1852KB,可继续优化):
#include <iostream>
#include <fstream>
#include <cstring> // memset
#include <cmath> // abs
#include <algorithm> // min
using namespace std;
const int d2 = 22, d1 = 502, INF(0x3f3f3f3f);
int dp[d2][d1];
bool has[d2][d1];
// 初始化全局变量
void init(int row) {
// 会浪费很多时间么?这个简单的初始化能不能再优化?
/*
for (int i = 0; i <= row; ++i) {
for (int j = 0; j < d1; ++j) {
dp[i][j] = INF;
has[i][j] = false;
}
}
*/
memset(has, false, sizeof has);
memset(dp, 0x3f, sizeof dp); // 一开始就赋以最大值
}
int main() {
// fstream cin("in.txt");
int times, num;
while (cin >> times >> num) {
init(times);
for (int i = 1; i <= times; ++i) {
for (int j = 1; j <= num; ++j) {
int tmp;
cin >> tmp;
has[i][tmp] = true; // 该位置上有地鼠
if (i == 1) {
// 特殊的初始化
// 这是必须的,因为在数塔顶层打地鼠,代价为0
// 也可以这样想,一开始dp数组设成最大值,而代价是累加的,如果没有特殊初始化,最终结果一定是很大的数字
dp[1][tmp] = 0;
}
}
}
for (int i = 2; i <= times; ++i) {
for (int j = 1; j <= 500; ++j) {
if (has[i][j]) { // 走到这里
for (int k = 1; k <= 500; ++k) { // 其实这里可以优化,毕竟距离相差最小的那个就可以了,不过代码要写长一点
if (has[i-1][k]) { // 从这里出发
dp[i][j] = min(dp[i][j], dp[i-1][k] + abs(j - k));
}
}
}
}
}
int result(INF);
for (int i = 1; i <= 500; ++i) {
result = min(result, dp[times][i]);
}
cout << result << endl;
}
return 0;
}