📜  桥梁和火炬问题程序

📅  最后修改于: 2021-04-29 17:07:01             🧑  作者: Mango

给定一个正整数,表示“ n”个人的穿越时间。这些“ n”个人站在桥的一侧。桥一次最多可容纳两个人。当两个人过桥时,他们必须以较慢的人的速度移动。找到所有人都可以过桥的最短总时间。看到这个难题以了解更多
注意:较慢的人的步调是由较大的时间给出的。

Input:  Crossing Times = {10, 20, 30}
Output: 60
Explanation
1. Firstly person '1' and '2' cross the bridge
   with total time about 20 min(maximum of 10, 20) 
2. Now the person '1' will come back with total 
   time of '10' minutes.
3. Lastly the person '1' and '3' cross the bridge
   with total time about 30 minutes
Hence total time incurred in whole journey will be
20 + 10 + 30 = 60

Input: Crossing Times = [1, 2, 5, 8}
Output: 15
Explanation
See this for full explanation.

该方法是使用动态编程。在深入研究动态programminc之前,让我们看一下解决问题所需要的以下观察结果。

  1. 当任何两个人过桥时,最快的人过境时间将不会被回答,因为他们两个人的移动速度都是最慢的。
  2. 当一些人越过河流到达右侧时,只有最快的人(最小的整数)会回到左侧。
  3. 人员只能出现在桥的左侧或右侧。因此,如果我们保持左掩码,则可以通过将左掩码中不存在的位设置为“ 1”来轻松计算右掩码。例如,Right_mask =(((2 n )– 1)XOR(left_mask)。
  4. 任何人都可以很容易地用位掩码(通常称为“掩码”)表示。当设置“掩码”的第i个位时,这意味着该人位于桥的左侧,否则它将出现在桥的右侧。例如,假设6个人的掩码为100101,则代表人1、4、6位于桥的左侧,而人2、3和5位于桥的右侧。
// C++ program to find minimum time required to
// send people on other side of bridge
#include 
using namespace std;
  
/* Global dp[2^20][2] array, in dp[i][j]--
   'i' denotes mask in which 'set bits' denotes
   total people standing at left side of bridge
   and 'j' denotes the turn that represent on 
   which side we have to send people either
   from left to right(0) or from right to 
   left(1)  */
int dp[1 << 20][2];
  
/* Utility function to find total time required
   to send people to other side of bridge */
int findMinTime(int leftmask, bool turn, int arr[], int& n)
{
  
    // If all people has been transfered
    if (!leftmask)
        return 0;
  
    int& res = dp[leftmask][turn];
  
    // If we already have solved this subproblem, 
    // return the answer.
    if (~res)
        return res;
  
    // Calculate mask of right side of people
    int rightmask = ((1 << n) - 1) ^ leftmask;
  
    /* if turn == 1 means currently people are at
     right side, thus we need to transfer
     people to the left side */
    if (turn == 1) {
        int minRow = INT_MAX, person;
        for (int i = 0; i < n; ++i) {
  
            // Select one people whose time is less
            // among all others present at right
            // side
            if (rightmask & (1 << i)) {
                if (minRow > arr[i]) {
                    person = i;
                    minRow = arr[i];
                }
            }
        }
  
        // Add that person to answer and recurse for next turn
        // after initializing that person at left side
        res = arr[person] + findMinTime(leftmask | (1 << person),
                                        turn ^ 1, arr, n);
    }
    else {
  
        // __builtin_popcount() is inbuilt gcc function
        // which will count total set bits in 'leftmask'
        if (__builtin_popcount(leftmask) == 1) {
            for (int i = 0; i < n; ++i) {
  
                // Since one person is present at left
                // side, thus return that person only
                if (leftmask & (1 << i)) {
                    res = arr[i];
                    break;
                }
            }
        }
        else {
  
            // try for every pair of people by
            // sending them to right side
  
            // Initialize the result with maximum value
            res = INT_MAX;
            for (int i = 0; i < n; ++i) {
  
                // If ith person is not present then
                // skip the rest loop
                if (!(leftmask & (1 << i)))
                    continue;
  
                for (int j = i + 1; j < n; ++j) {
                    if (leftmask & (1 << j)) {
  
                        // Find maximum integer(slowest
                        // person's time)
                        int val = max(arr[i], arr[j]);
  
                        // Recurse for other people after un-setting
                        // the ith and jth bit of left-mask
                        val += findMinTime(leftmask ^ (1 << i) ^ (1 << j),
                                                       turn ^ 1, arr, n);
                        // Find minimum answer among
                        // all chosen values
                        res = min(res, val);
                    }
                }
            }
        }
    }
    return res;
}
  
// Utility function to find minimum time
int findTime(int arr[], int n)
{
    // Find the mask of 'n' peoples
    int mask = (1 << n) - 1;
  
    // Initialize all entries in dp as -1
    memset(dp, -1, sizeof(dp));
  
    return findMinTime(mask, 0, arr, n);
}
  
// Driver program
int main()
{
    int arr[] = { 10, 20, 30 };
    int n = sizeof(arr)/sizeof(arr[0]);
    cout << findTime(arr, n);
    return 0;
}
Output 
60

时间复杂度: O(2^n \cdot n \cdot n)
辅助空间: O(2^n)