2013-06-29 21:46:08Morris

[UVA][dp] 11157 - Dynamic Frog

D

Next Generation Contest 3

Time Limit: 2 seconds

Dynamic Frog

 

With the increased use of pesticides, the local streams and rivers have become so contaminated that it has become almost impossible for the aquatic animals to survive.

Frog Fred is on the left bank of such a river. N rocks are arranged in a straight line from the left bank to the right bank. The distance between the left and the right bank is D meters. There are rocks of two sizes. The bigger ones can withstand any weight but the smaller ones start to drown as soon as any mass is placed on it. Fred has to go to the right bank where he has to collect a gift and return to the left bank where his home is situated.

He can land on every small rock at most one time, but can use the bigger ones as many times as he likes. He can never touch the polluted water as it is extremely contaminated.

Can you plan the itinerary so that the maximum distance of a single leap is minimized?

 

Input

The first line of input is an integer T(T<100) that indicates the number of test cases. Each case starts with a line containing two integers N(0≤N≤100)and D(1≤D≤1000000000). The next line gives the description of the N stones. Each stone is defined by S-M. S indicates the type Big(B) or Small(S) and M(0<M<D) determines the distance of that stone from the left bank. The stones will be given in increasing order of M.

Output

For every case, output the case number followed by the minimized maximum leap.

Sample Input

Output for Sample Input

3
1 10
B-5
1 10
S-5
2 10
B-3 S-6
Case 1: 5
Case 2: 10
Case 3: 7

 

Problem Setter: Sohel Hafiz
Special Thanks: Jane Alam Jan


題目描述:

大石頭可以無限踩, 小石頭只能採一次, 青蛙從左岸跳到右岸, 再跳回左岸,
問其中最大的步伐的最小值為何?

題目解法:

先說明一下, Greedy 也是行得通的, 但這裡我使用 dp 去解決,
小石頭當作一個點, 大石頭當作兩個點, 排序過後,
相當於一次從左岸走兩條路徑到達右岸,
dp[i][j] 表示第一條路徑到達 i 且 第二條路徑到達 j, 轉換方程如下表示。


#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;

int main() {
    int testcase, n, D, cases = 0;
    char s[50];
    int i, j, k;
    scanf("%d", &testcase);
    while(testcase--) {
        scanf("%d %d", &n, &D);
        int A[305], m = 0, x;
        for(i = 0; i < n; i++) {
            scanf("%s", s);
            sscanf(s+2, "%d", &x);
            A[m++] = x;
            if(s[0] == 'B')
                A[m++] = x;
        }
        A[m++] = 0;
        A[m++] = D, A[m++] = D;
        sort(A, A+m);
        int dp[305][305];
#define oo 2147483647
        for(i = 0; i < m; i++)
            for(j = 0; j < m; j++)
                dp[i][j] = oo;
        dp[0][0] = 0;
        for(i = 0; i < m; i++) {
            for(j = 0; j < m; j++) {
                k = max(i, j)+1;
                dp[i][k] = min(dp[i][k], max(dp[i][j], A[k]-A[j]));
                dp[k][j] = min(dp[k][j], max(dp[i][j], A[k]-A[i]));
            }
        }
        printf("Case %d: %d\n", ++cases, dp[m-1][m-2]);
    }
    return 0;
}

kai 2014-04-28 22:02:05

大大您好:
for(i = 0; i < m; i++) {
for(j = 0; j < m; j++) {
k = max(i, j)+1;
dp[i][k] = min(dp[i][k], max(dp[i][j], A[k]-A[j]));
dp[k][j] = min(dp[k][j], max(dp[i][j], A[k]-A[i]));
}
}
這段看不太懂,可以請您解釋一下嗎?

版主回應
首先,思考 dp[i][j] 表示的含義,如果無法理解就更無法明白遞迴方程。

dp[i][j] 是兩條路徑 0 ~> i 和 0 ~> j, 其中這兩個經過的路徑不會有相同的點,而且所有點編號 <= max(i, j) 都被走過。題目問的是最大步伐的最小值,意涵著每一顆石頭都被走過,可以用矛盾證明 - 如果少走一個,不會比較優。

明白這些後,回過頭來思考,現在的分界在 max(i, j) 下一個走過的點,一定是 max(i, j)+1,但是不確定是哪一個路徑去銜接它,所以

dp[i][k] = min(dp[i][k], max(dp[i][j], A[k]-A[j]));
dp[k][j] = min(dp[k][j], max(dp[i][j], A[k]-A[i]));

而題目要求迴路,所以增了兩次的終點,來滿足兩條路徑可以到達同一個點。
2014-04-29 15:40:13