The Great Wall of China is truly one of the greatest wonders of the world. In 3-rd century BC, Emperor
Qin Shi Huang connected the defensive structures built earlier by the states of Qin, Yan, and Zhao
kingdoms. The purpose of the wall was to defend against raids by the barbarians from Mongolia and
Manchuria. The wall was extended and renovated in later centuries, creating an impressive 6,700 km
long fortification.
The centuries have left their mark on the wall, there are several sections that need immediate
renovation. These sections have to be repaired as soon as possible since they deteriorate every day: if
we do not fix them now, it will be more expensive to repair them later. Thus the Ministry of Monuments
have designed and built the world’s first Great Wall Automatic Repair Robot (GWARR), to repair the
damaged sections (we are in the 21-st century, aren’t we?) Your task is to write the software that will
guide the robot and decide the order in which the sections are to be repaired.
For the purpose of this problem, we assume that the Great Wall is a long straight line, and every
location on the wall is identified by a single number (say, the distance from one end). The GWARR is
placed at some location on the wall and it can move with constant speed in both directions. For each
damaged section you are given its location, how much it would cost to repair now, and how the cost
would increase if repaired later. The GWARR works so efficiently that once it is at the exact location
of the damaged section it can repair the wall immediately.
Input
The input contains several blocks of test cases. Each case begins with a line containing three integers:
an integer 1 ≤ n ≤ 1000, the number of damaged sections, an integer 1 ≤ v ≤ 100, the speed of
the GWARR in distance units/time units, and an integer 1 ≤ x ≤ 500000, the initial position of
the GWARR. The next n lines describe the n damaged sections that have to be repaired. Each line
contains three integers: the location 1 ≤ x ≤ 500000 of the section, the cost 0 ≤ c ≤ 50000 of repairing
it immediately, and 1 ≤ ∆ ≤ 50000, the increase in cost per time unit. Therefore, if the section is
repaired after t time units have passed, then we have to pay c + t∆ units of money. It can be assumed
that the locations of the sections are all different, and the initial location of the robot is not on the list
of damaged seetions.
The input is terminated by a test case with n = v = x = 0.
Output
For each test case, you have to output a line containing a single number, the minimum cost of repairing
the wall. This number should be an integer, round down the result, if necessary. It can be assumed
that the minimum cost is not more than 1000000000.
In the optimum solution for the first test case below, we first fix loeation 998 at the cost of 600,
then the location 1010 at the cost of l400, and finally we fix the location 996 at the cost of 84, giving
the total cost 2084.
Sample Input
3 1 1000
1010 0 100
998 0 300
996 0 3
3 1 1000
1010 0 100
998 0 3
996 0 3
0 0 0
Sample Output
2084
1138
一个机器人去修长城,有n个维修点,机器人移动速度为v,初始坐标为x,然后是n组维修点数据:维修点的坐标,维修这个点的固有费用,维修费用随时间的增长率。
第一反应就是区间dp,因为之前刚做过一个区间dp(Alibaba那篇博文我写了区间dp的讲解),很容易想到,然后的想法就是写两个区间dp,一个是时间Time[i][j][2],表示区间ij之间的最短时间,然后再创一个dp[i][j][2],就是ij之间的最少费用。
转移方程就是
Time[i][j][0]=min(Time[i+1][j][0]+node[i+1].x-node[i].x,Time[i+1][j][1]+node[j].x-node[i].x);
Time[i][j][0]-min(Time[i][j-1][0]+node[j].x-node[i].x,Time[i][j-1][1]+node[j].x-node[j-1].x);
value[i][j][0] = node[i].c + min(value[i + 1][j][0] + time1*node[i].dit, value[i + 1][j][1] + time2*node[i].dit);
value[i][j][1] = node[j].c + min(value[i][j - 1][0] + time3*node[j].dit, value[i][j - 1][1] + time4*node[j].dit);
time1,time2,time3,time4就是对应的时间,我直接代替了。
#include
#include
#include
#include
#include
#include
#include
using namespace std;
double extime[1005][1005][2];
double value[1005][1005][2];
struct Node{
int x;
int c;
int dit;
}node[1005];
bool cmp(Node a, Node b)
{
return a.x < b.x;
}
int main()
{
int i, j, m, n, ans, t, v, x;
while (scanf("%d%d%d", &n, &v, &x) != EOF)
{
if (!n&&!x&&!v)break;
for (i = 1; i <= n; i++)
cin >> node[i].x >> node[i].c >> node[i].dit;
sort(node + 1, node + 1 + n, cmp);
for (i = 1; i <= n; i++)
{
extime[i][i][1] = extime[i][i][0] = abs(x - node[i].x)*1.0 / v;
value[i][i][0] = value[i][i][1] = node[i].c*1.0 + node[i].dit*extime[i][i][0] * 1.0;
}
for (i = n - 1; i >= 1; i--)
{
for (j = i + 1; j <= n; j++)
{
extime[i][j][0] = min(extime[i + 1][j][0] * 1.0 + (node[i + 1].x - node[i].x)*1.0 / v, extime[i + 1][j][1] * 1.0 + (node[j].x - node[i].x)*1.0 / v);
extime[i][j][1] = min(extime[i][j - 1][0] * 1.0 + (node[j].x - node[i].x)*1.0 / v, extime[i][j - 1][1] * 1.0 + (node[j].x - node[j - 1].x)*1.0 / v);
}
}
for (i = n - 1; i >= 1; i--)
{
for (j = i + 1; j <= n; j++)
{
double time1, time2, time3, time4;
time1 = (node[i + 1].x - node[i].x)*1.0 / v + extime[i + 1][j][0];
time2 = (node[j].x - node[i].x)*1.0 / v + extime[i + 1][j][1];
time3 = (node[j].x - node[i].x)*1.0 / v + extime[i][j - 1][0];
time4 = (node[j].x - node[j - 1].x)*1.0 / v + extime[i][j - 1][1];
value[i][j][0] = node[i].c*1.0 + min(value[i + 1][j][0] * 1.0 + time1*node[i].dit*1.0, value[i + 1][j][1] * 1.0 + time2*node[i].dit*1.0);
value[i][j][1] = node[j].c*1.0 + min(value[i][j - 1][0] * 1.0 + time3*node[j].dit*1.0, value[i][j - 1][1] * 1.0 + time4*node[j].dit*1.0);
}
}
ans = min(value[1][n][0], value[1][n][1]);
cout << ans << endl;
}
return 0;
}
但是wa掉了,看了网上的题解,发现别人都是用了一次区间dp,然后我就画了画转移,发现时间和费用的转移并不一定完全一致,意思就是在这个区间,时间最短的路线和费用最短的路线可能不一样。比如左端时间少,右端时间长,但是左端增长率非常低,右端增长率异常高,那么转移就不一样了。这样时间就不可以直接用了。
正确做法是,每走一步,算一次时间。意思就是,当前移动花费t时间,那么所有未维修的站点的费用都要增加node[i].dit*t,对于每一次的转移都是这样,其实就是把转移的时间分解,逐步增加这个维修点的费用,比如维修1,2,3站点,先修1,时间t1,再修2,时间t2,那么对于站点3,维修之前,花了t1+t2时间,先算出t1时间,再算出t2时间,加起来还是总的费用。
#include
#include
#include
#include
#include
#include
#include
using namespace std;
double value[1005][1005][2];
int d[1005];
int n;
struct Node{
int x;
int c;
int dit;
}node[1005];
bool cmp(Node a, Node b)
{
return a.x < b.x;
}
double Cost(int l, int r, double temp)
{
double ans = 0;
ans = (d[n] - d[r] + d[l - 1])*1.0*temp;
return ans;
}
int main()
{
int i, j, m, ans, t, v, x;
double Time;
while (scanf("%d%d%d", &n, &v, &x) != EOF)
{
if (!n&&!x&&!v)break;
for (i = 1; i <= n; i++)
scanf("%d%d%d", &node[i].x, &node[i].c, &node[i].dit);
sort(node + 1, node + 1 + n, cmp);
d[0] = 0;
d[1] = node[1].dit;
for (i = 2; i <= n; i++)
d[i] = d[i - 1] + node[i].dit;
for (i = 1; i <= n; i++)
{
Time = abs(x - node[i].x)*1.0 / v;
value[i][i][0] = value[i][i][1] = node[i].c*1.0 + node[i].dit*Time + Cost(i, i, Time);
}
for (i = n - 1; i >= 1; i--)
{
for (j = i + 1; j <= n; j++)
{
double time1, time2, time3, time4;
time1 = (node[i + 1].x - node[i].x)*1.0 / v;
time2 = (node[j].x - node[i].x)*1.0 / v;
time3 = (node[j].x - node[i].x)*1.0 / v;
time4 = (node[j].x - node[j - 1].x)*1.0 / v;
value[i][j][0] = node[i].c*1.0 + min(value[i + 1][j][0] * 1.0 + time1*node[i].dit*1.0 + Cost(i, j, time1), value[i + 1][j][1] * 1.0 + time2*node[i].dit*1.0 + Cost(i, j, time2));
value[i][j][1] = node[j].c*1.0 + min(value[i][j - 1][0] * 1.0 + time3*node[j].dit*1.0 + Cost(i, j, time3), value[i][j - 1][1] * 1.0 + time4*node[j].dit*1.0 + Cost(i, j, time4));
}
}
ans = min(value[1][n][0], value[1][n][1]);
printf("%d\n", ans);
}
return 0;
}
还要注意这个Cost函数,我之前是从1到l-1,r+1到n来计算这个未维修的增长率,会超时的,这里用了优化技巧,作为一个tips使用。
其实就是求[l,r]区间,可以求出[0,l],[0,r],这样减一下就是要求的区间了。而求这样的区间是有统一方法的。