思路
要想最终代价最低,就不能跳跃着修复,也就是经过一段时间后已经修复好的破损应是一段连续区间。
每次只有两个决策:向左走或者向右走。根据这个可以设计出状态:
dp(i,j,k)表示修好(i,j)后机器人停留在k(0表示在左端,1表示在右端)端的费用。
另外, c 值的总和是固定的费用,不论决策如何,最终都是要加的。因此不用加入状态转移。不过最后不要忘了加上它
修复某处破损的代价虽然不是定值,但却是随着时间线性增长的,所以当修复完一处或一段破损时,修复其他破损的费用可以算出来,只需将其累加到当前状态即可,也可以视作修复某处破损产生的时间代价。
记忆化搜索
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 1000 + 5;
const double INF = 1e30;
struct Section {
double x, c, dt;
bool operator < (const Section& rhs) const {
return x < rhs.x;
}
} s[maxn];
int kase, n;
int vis[maxn][maxn][2];
double v, x, d[maxn][maxn][2];
double sum_dt[maxn]; // prefix sum of dt
double cost(double x1, double x2, int i, int j){
double finish_dt = 0;
if(i >= 1&&j >= 1) finish_dt += sum_dt[j] - sum_dt[i-1];
return (sum_dt[n] - finish_dt) * fabs(x2 - x1)/v;
}
double dp(int i, int j, int k){
if(i == 1&&j == n) return 0;
double& ans = d[i][j][k];
if(vis[i][j][k] == kase) return ans;
vis[i][j][k] = kase;
double pos = (k == 0? s[i].x : s[j].x);
ans = INF;
if(i > 1) ans = min(ans, dp(i-1, j, 0) + cost(pos, s[i-1].x, i, j));
if(j < n) ans = min(ans, dp(i, j+1, 1) + cost(pos, s[j+1].x, i, j));
return ans;
}
int main()
{
memset(vis, 0, sizeof(vis));
freopen("in.txt","r",stdin);
while(scanf("%d %lf %lf", &n, &v, &x) == 3 && n){
++kase;
double sumc = 0;
for(int i = 1; i <= n; i++) {
scanf("%lf %lf %lf", &s[i].x, &s[i].c, &s[i].dt);
sumc += s[i].c;
}
sort(s+1, s+n+1); // in increasing order of position
sum_dt[0] = 0;
for(int i = 1; i <= n; ++i) sum_dt[i] = sum_dt[i-1] + s[i].dt;
s[0].x = -INF;
s[n+1].x = INF;
double ans = INF;
for(int i = 1; i <= n+1; ++i){
if(x > s[i-1].x&&x < s[i].x){
if(i > 1) ans = min(ans, dp(i-1, i-1, 0) + cost(x, s[i-1].x, -1, -1));
if(i <= n) ans = min(ans, dp(i, i, 0) + cost(x, s[i].x, -1, -1));
break;
}
}
printf("%d\n",(int)(ans+sumc));
//printf("%.0lf\n", floor(ans + sumc));
}
return 0;
}
递推:
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 1000+5;
const double INF = 1e30;
struct Section {
double x, c, dt;
bool operator < (const Section& rhs) const {
return x < rhs.x;
}
} s[maxn];
//int vis[maxn][maxn][2];
double d[maxn][maxn][2], x, v;
double sum_dt[maxn]; // prefix sum of dt
int n;
int main()
{
//freopen("in.txt","r",stdin);
while(scanf("%d%lf%lf", &n, &v, &x) == 3 && n){
//++kase;
for(int i = 0;i<=n+1;i++){
for(int j = 0;j<=n+1;j++){
d[i][j][0] = INF;
d[i][j][1] = INF;
}
}
double sumc = 0;
for(int i = 1; i <= n; i++) {
scanf("%lf %lf %lf", &s[i].x, &s[i].c, &s[i].dt);
sumc += s[i].c;
}
s[n+1].x = x; s[n+1].c = s[n+1].dt = 0; // 小技巧,把初始点也加入其中,但设置花费为0
sort(s+1, s+n+2); // in increasing order of position
// 计算前缀和
sum_dt[0] = 0;
for(int i = 1; i <= n+1; ++i) sum_dt[i] = sum_dt[i-1] + s[i].dt;
// 寻找初始点
int start;
for(int i = 1; i <= n+1; ++i){
if(s[i].x == x){
start = i; break;
}
}
//printf("%d\n",start);
d[start][start][0] = d[start][start][1] = 0;
for(int i = start; i >= 1; --i){
for(int j = start; j <= n+1; ++j){
//double finish_dt = sum_dt[j] - sum_dt[i-1];
double h = (i==1? 0 : sum_dt[i-1]) + sum_dt[n+1] - sum_dt[j];
if(i > 1){ // 向左
double& ans = d[i-1][j][0];
double cost = d[i][j][0] + ((s[i].x - s[i-1].x) / v) * h ;
ans = min(ans, cost);
cost = d[i][j][1] + ((s[j].x - s[i-1].x) / v) * h;
ans = min(ans, cost);
}
if(j < n+1){ // 向右
double& ans2 = d[i][j+1][1];
double cost = d[i][j][0] + ((s[j+1].x - s[i].x) / v) * h ;
ans2 = min(ans2,cost);
cost = d[i][j][1] + ((s[j+1].x - s[j].x) / v) * h;
ans2 = min(ans2, cost);
}
}
}
double ans = min(d[1][n+1][0], d[1][n+1][1]) + sumc;
printf("%d\n",(int)(ans));
}
return 0;
}