九度oj 题目1086:最小花费

题目描述:
在某条线路上有N个火车站,有三种距离的路程,L1,L2,L3,对应的价格为C1,C2,C3.其对应关系如下:
距离s           票价
0<S<=L1         C1
L1<S<=L2        C2
L2<S<=L3        C3
输入保证0<L1<L2<L3<10^9,0<C1<C2<C3<10^9。
每两个站之间的距离不超过L3。
当乘客要移动的两个站的距离大于L3的时候,可以选择从中间一个站下车,然后买票再上车,所以乘客整个过程中至少会买两张票。
现在给你一个 L1,L2,L3,C1,C2,C3。然后是A B的值,其分别为乘客旅程的起始站和终点站。
然后输入N,N为该线路上的总的火车站数目,然后输入N-1个整数,分别代表从该线路上的第一个站,到第2个站,第3个站,……,第N个站的距离。
根据输入,输出乘客从A到B站的最小花费。
输入:
以如下格式输入数据:
L1  L2  L3  C1  C2  C3
A  B
N
a[2]
a[3]
……
a[N]
输出:
可能有多组测试数据,对于每一组数据,
根据输入,输出乘客从A到B站的最小花费。
样例输入:
1 2 3 1 2 3
1 2
2
2
样例输出:
2

这道题真是一个惨痛的回忆。
开始的思路是普通的动态规划思路,即经典的分割问题,最后通过的代码如下
 1 #include <cstdio>
 2 #include <cstdlib>
 3 #include <cstring>
 4 #include <string>
 5 #define MAX 300
 6 #define inf 10000000009
 7 
 8 long long minCost[MAX][MAX];
 9 long long dis[MAX][MAX];
10 long long dis0[MAX];
11 
12 int main(int argc, char const *argv[])
13 {
14     long long L1, L2, L3, C1, C2, C3;
15     //freopen("input.txt","r",stdin);
16     while(scanf("%lld %lld %lld %lld %lld %lld",&L1, &L2, &L3, &C1, &C2, &C3) != EOF)
17     {
18         int A, B;
19         int N;
20         scanf("%d %d",&A,&B);
21         scanf("%d",&N);
22         dis0[1] = 0;
23         for(int i = 2; i <= N; i++) {
24             scanf("%lld",&dis0[i]);
25         }
26         for(int l = 1; l <= N - 1; l++) {
27             //length from 1 to N - 1
28             for(int i = 1; i <= N - l; i++) {
29                 //start from i to N - l
30                 int j = i + l;
31                 //end is i + l
32                 dis[i][j] = dis0[j] - dis0[i];
33                 if(dis[i][j] <= L1) {
34                     minCost[i][j] = C1;
35                 }
36                 else if(dis[i][j] <= L2) {
37                     minCost[i][j] = C2;
38                 }
39                 else if(dis[i][j] <= L3) {
40                     minCost[i][j] = C3;
41                 }
42                 else {
43                     minCost[i][j] = inf;
44                 }
45                 
46                 for(int k = i + 1; k <= j-1; k++) {
47                     long long int q = minCost[i][k] + minCost[k][j];
48                     if(q < minCost[i][j]) {
49                         minCost[i][j] = q;
50                     }
51                 }
52             }
53         }
54         if(A < B) {
55             printf("%lld
",minCost[A][B]);
56         }
57         else if(A > B){
58             printf("%lld
",minCost[B][A]);
59         }
60         else {
61             printf("%d
",0);
62         }
63 
64     }
65     return 0;
66 }

但代码一开始提交了n次都没有通过,经过很长时间的排查,终于发现了原因。一开始我设的inf是

1000000009,WA改成了

10000000009后就AC了。

之后我发现没必要去算出每两个点之间的最小花费,只需要计算出从A到B之间的最小花费即可,dis数组也不需要

代码如下:

 1 #include <cstdio>
 2 #include <cstdlib>
 3 #include <cstring>
 4 #include <string>
 5 #define MAX 300
 6 #define inf 10000000009
 7 
 8 long long minCost[MAX][MAX];
 9 long long dis0[MAX];
10 
11 int main(int argc, char const *argv[])
12 {
13     long long L1, L2, L3, C1, C2, C3;
14     //freopen("input.txt","r",stdin);
15     while(scanf("%lld %lld %lld %lld %lld %lld",&L1, &L2, &L3, &C1, &C2, &C3) != EOF)
16     {
17         int A, B;
18         int N;
19         scanf("%d %d",&A,&B);
20         scanf("%d",&N);
21         
22         for(int i = 2; i <= N; i++) {
23             scanf("%lld",&dis0[i]);
24         }
25         if(A > B) {
26             int temp = A;
27             A = B;
28             B = temp;
29         }
30         long long n = B - A;
31         dis0[1] = 0;
32         minCost[A][A] = 0;
33         for(int l = 1; l <= n; l++) {
34             //length from 1 to N - 1
35             for(int i = A; i <= A + n - l; i++) {
36                 //start from i to N - l
37                 int j = i + l;
38                 //end is i + l
39                 long long temp = dis0[j] - dis0[i];
40                 if(temp <= L1) {
41                     minCost[i][j] = C1;
42                 }
43                 else if(temp <= L2) {
44                     minCost[i][j] = C2;
45                 }
46                 else if(temp <= L3) {
47                     minCost[i][j] = C3;
48                 }
49                 else {
50                     minCost[i][j] = inf;
51                 }
52                 
53                 for(int k = i + 1; k <= j-1; k++) {
54                     long long int q = minCost[i][k] + minCost[k][j];
55                     if(q < minCost[i][j]) {
56                         minCost[i][j] = q;
57                     }
58                 }
59             }
60         }
61         printf("%lld
",minCost[A][B]);
62 
63     }
64     return 0;
65 }

 但是我们研究一下会发现,如果两地的距离小于L3,则它们之间的最小花费必然在C1,C2,C3之间,所以若将minCost改为一维数组,将i作为中间点,j作为终点,当i到j的距离小于L3时,minCost[j] = min (minCost[j], minCost[i] + Ci); 所以遍历i时,选取与其距离小于L3的j,即可。代码如下:

 1 #include <cstdio>
 2 #include <cstdlib>
 3 #include <cstring>
 4 #include <string>
 5 #define MAX 150
 6 #define inf 10000000009
 7 
 8 long long minCost[MAX];
 9 long long dis0[MAX];
10 
11 int main(int argc, char const *argv[])
12 {
13     long long L1, L2, L3, C1, C2, C3;
14     //freopen("input.txt","r",stdin);
15     while(scanf("%lld %lld %lld %lld %lld %lld",&L1, &L2, &L3, &C1, &C2, &C3) != EOF)
16     {
17         int A, B;
18         int N;
19         
20         scanf("%d %d",&A,&B);
21         scanf("%d",&N);
22         dis0[1] = 0;
23         for(int i = 2; i <= N; i++) {
24             scanf("%lld",&dis0[i]);
25             minCost[i] = inf;
26         }
27         if(A > B) {
28             int temp = A;
29             A = B;
30             B = temp;
31         }
32         minCost[A] = 0;
33         for(int i = A; i < B; i++) {
34             for(int j = i + 1; j <= B; j++) {
35                 long long int temp;
36                 if(dis0[j] - dis0[i] <= L1) {
37                     temp = C1;
38                 }
39                 else if(dis0[j] - dis0[i] <= L2) {
40                     temp = C2;
41                 }
42                 else if(dis0[j] - dis0[i] <= L3) {
43                     temp = C3;
44                 }
45                 else {
46                     break;
47                 }
48                 long long q = minCost[i] + temp;
49                 if(minCost[j] > q) {
50                     minCost[j] = q;
51                 }
52             }
53         }
54         printf("%lld
",minCost[B]);
55     }
56     return 0;
57 }

 因为两个站的最大距离不超过L3,所以从A站到j站,中间必然会经过距离j站距离小于L3的某站,故minCost[j]在遍历时中间的分割点必然在这些站之间,所以可以这样化简

原文地址:https://www.cnblogs.com/jasonJie/p/5682183.html