题目
思路
首先将修缮点按坐标从小到大排序,则已修复的点一定是一个连续的区间。这样就可以用d(i,j,k)表示已经修复完(i,j),机器人现在在k(k=0左边缘,k=1右边缘)时已经发生的总费用。
那么就有一个问题,费用根据时间计算,而状态里没有时间,那下一个修缮点的费用该如何计算?
此处类比颜色长度。由于所有修缮点肯定都要被修,那么我们先把所有的立刻修缮费用加起来作为初状态,每次机器人移动耗费了时间,就把耗费的时间乘以(i,j)外面的点的单位增加修缮费用,即可。
决策只有两种,往修一个点,或往右修一个点。
本题迷之WA,调了一晚上对拍了很久,只发现有小数精度的问题——std与my的ans只差1。我没法解决,所有本题仅需理解未来费用就行了,实际代码实现不用太过在意。
代码
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#define ll long long
#define ld long double
#define _for(i,a,b) for(int i = (a); i<(b); i++)
#define _rep(i,a,b) for(int i = (a); i<=(b); i++)
using namespace std;
const ld eps = 1e-5;
const ll INF = 1000000000000000; // INF定义成10000000,WA
const int maxn = 1000 + 10;
struct damage {
int x, c, d;
bool operator < (const struct damage &rhs) const {
return x < rhs.x;
}
}node[maxn];
int n, v, startx, sumc, vis[maxn][maxn][2];
ll sumd[maxn];
ld d[maxn][maxn][2];
// (i,j)已经修复,k=1表示机器在i,k=2表示机器在j
ld dp(int i, int j, int k) {
if (i > j || i < 1 || j > n) return INF;
ld &ans = d[i][j][k];
if (vis[i][j][k]) return ans;
ld ans1, ans2;
if (k == 1) {
ans1 = dp(i, j - 1, 0) + (sumd[i - 1] + sumd[n] - sumd[j - 1])*(abs((node[j].x - node[i].x)) / (ld)v);
ans2 = dp(i, j - 1, 1) + (sumd[i - 1] + sumd[n] - sumd[j - 1])*(abs((node[j].x - node[j-1].x)) / (ld)v);
}
else {
ans1 = dp(i + 1, j, 1) + (sumd[i] + sumd[n] - sumd[j])*abs((node[j].x - node[i].x)) / (ld)v;
ans2 = dp(i + 1, j, 0) + (sumd[i] + sumd[n] - sumd[j])*abs((node[i+1].x - node[i].x)) / (ld)v;
}
ans = min(ans1, ans2);
if (ans > INF) ans = INF;
vis[i][j][k] = 1;
return ans;
}
int main() {
while (scanf("%d%d%d", &n, &v, &startx) == 3 && n && v && startx) {
_rep(i, 1, n)
scanf("%d%d%d", &node[i].x, &node[i].c, &node[i].d);
sort(node + 1, node + 1 + n);
// 计算dd的前缀和,sumd
_rep(i, 1, n) sumd[i] = sumd[i - 1] + node[i].d;
// 计算所有立即修复的费用,sumc
sumc = 0;
_rep(i, 1, n) sumc += node[i].c;
memset(vis, 0, sizeof(vis));
// 计算初状态
int pos1, pos2;
damage robot; robot.x = startx;
damage* np = lower_bound(node + 1, node + 1 + n, robot);
pos1 = np - node;
pos2 = pos1 - 1;
if (pos1 <= n + 1) {
d[pos1][pos1][0] = d[pos1][pos1][1] = sumc + (ld)sumd[n] * abs(startx - node[pos1].x) / (ld)v;
vis[pos1][pos1][0] = vis[pos1][pos1][1] = 1;
}
if (pos2 >= 0) {
d[pos2][pos2][0] = d[pos2][pos2][1] = sumc + (ld)sumd[n] * abs(startx - node[pos2].x) / (ld)v;
vis[pos2][pos2][0] = vis[pos2][pos2][1] = 1;
}
printf("%.0lf\n", floor(min(dp(1, n, 0), dp(1, n, 1)))); // 注意,floor(double)的返回值还是double
}
return 0;
}