乘坐木筏问题

/**
问题描述:
一个公司的所有员工正在风景秀丽的海南岛旅游,计划行程准备从三亚码头渡海去
蜈支洲岛,但由于最近的台风“琼花”摧毁了所有的渡船,游轮,轮船,帆船等,只
有一种正方形的木筏还在运营中。

这种木筏有N排座位,编号从1到N(N可能是一个两位数);每排有N列座位,编号为
(A, B, C, D ..), 这样每个座位都有唯一的对应号码,由一个数字和一个字母
组成,例如,“9C” 代表第9排第3个座位。但是有一些座位已经预先放上了空桶,
假设空桶没有分量,但该座位也不能再坐人了。另外还有一些座位已经分配给了
当地的渔民。为了航行安全,木筏渡海时必须保持平衡,所以必须满足以下条件:

(1) 前一半座位所坐的总人数要等于后一半座位所坐的总人数。
(2) 左一半座位所坐的总人数要等于右一半座位所坐的总人数。

现在,看如何来安排座位,使得木筏一次可以坐更多的公司员工。假设员工的数量
没有限制,并且每个人无论是员工还是渔民的重量都是相等的。

例如,有4*4的木筏,如下图所示:
   A  B  C  D
1 |.  S  S  S
2 |S  .  .  T
3 |.  T  .  .
4 |.  S  .  .

S是被空桶占着的座位,T是渔民的座位,.是空余的座位。
那么,最多还可以坐六名公司员工,保证前后左右分别都相等。
   A  B  C  D
1 |N  S  S  S
2 |S  N  N  T
3 |N  T  N  .
4 |.  S  N  .

N为我们的员工,左总人数=4=右总人数,前总人数=4=后总人数。
这样,总共最多可以安排6名员工,同时还能保证木筏平衡。

Write a function:

class Solution { public int solution(int N, String S, String T); }

给定木筏的大小N,和两个字符串分别代表桶的位置S和渔民的位置T,返回能
保障平衡的可容纳员工的最多人数。如果无论怎样也不能保障平衡,返回-1.

For instance,
given N=4, S="1B,1C,4B,1D,2A" and T="3B,2D",
your function should return 6.

Assume that:
(1) N is an even integer within the range [2..26];
(2) Strings S, T consist of valid seat numbers, separated with comma
    and no whitespace chars;
(3) Each seat number can only appear no more than once in the strings;
(4) Any seat number cannot appear in both S and T at the same time.
*/

// you can use includes, for example:
// #include <algorithm>

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>

#include <iostream>
#include <iosfwd>

using namespace std;

// you can write to stdout for debugging purposes, e.g.
// cout << "this is a debug message" << endl;
//
//    -----------------
//    |       |       |
//    |  1,i  |  2,j  |
//    |       |       |
//    -----------------
//    |       |       |
//    |  3,m  |  4,n  |
//    |       |       |
//    -----------------

int solution(int N, char* S, char* T)
{
    // write your code in C++14 (g++ 6.2.0)
    char *wood_boat = new char[N*N];
    memset(wood_boat, '.', N*N);
   
    const int H = N/2;  // 木筏子左上(1)右上(2)左下(3)右下(4)这四个象限的行数和列数
    const int Q = H*H;  // 每个象限的座位总数
    const int LEN_S = strlen(S), LEN_T = strlen(T);
   
    int S1 = 0, S2 = 0, S3 = 0, S4 = 0, /* 四个象限各自空桶的数量 */
        T1 = 0, T2 = 0, T3 = 0, T4 = 0, /* 四个象限各自渔民的数量 */
        E1 = 0, E2 = 0, E3 = 0, E4 = 0; /* 四个象限各自空位的数量(最重要的就是算出空位的数量) */
   
    int i = 0, j = 0, m = 0, n = 0;
   
    // Step-1: 计算每个象限中空桶的数量
    while (i < LEN_S)
    {
        int x = 0, y = 0;  // 任何一个空桶在木筏中的行列位置下标[0,N)
       
        // 确保不越界,第一位必须是数字
        assert(i < (LEN_S-1));
        assert(S[i] >= '1' && S[i] <= '9');
       
        if (S[i+1] >= '0' && S[i+1] <= '9')  /* 当前行号是一个两位数,例如'10C' */
        {
            // 确保不越界,下一位必须是字母
            assert(i < (LEN_S-2));
            assert(S[i+2] >= 'A' && S[i+2] <= 'Z');
           
            x = (S[i] - '0')*10 + S[i+1] - '1';   // 行号下标
            y = S[i+2] - 'A';                     // 列号下标
            i += 4;                               // 此种情况步长为4
        }
        else  /* 当前行号是个一位数 */
        {
            // 下一位必须是字母
            assert(S[i+1] >= 'A' && S[i+1] <= 'Z');
           
            x = S[i] - '1';         // 行号下标
            y = S[i+1] - 'A';       // 列号下标
            i += 3;                 // 此种情况步长为3
        }
       
        // 检查参数有效性和重复参数
        if (x >= N || y >= N)
        {
            std::cout << "\nERROR: Seat NO. out-of-range in second argument ["
                      << S << "](" << (x+1) << char('A'+y) << ")!\n";
            return -1;
        }
        if (wood_boat[x*N+y] != '.')
        {
            std::cout << "\nERROR: Duplicate seat NO. in second argument ["
                      << S << "](" << (x+1) << char('A'+y) << ")!\n";
            return -1;
        }
       
        // 标记木筏中空桶的位置'S'
        wood_boat[x*N+y] = 'S';
       
        // 判断当前空桶位于哪个象限,并且分别计数
        if      (x < H  && y <  H)   ++S1;
        else if (x < H  && y >= H)   ++S2;
        else if (x >= H && y <  H)   ++S3;
        else  /*(x >= H && y >= H)*/ ++S4;
    }// end while
   
    // Step-2: 计算每个象限中渔民的数量
    i = 0;
    while (i < LEN_T)
    {
        int x = 0, y = 0;  // 任何一个渔民在木筏中的行列位置下标[0,N)
       
        // 确保不越界,第一位必须是数字
        assert(i < (LEN_T-1));
        assert(T[i] >= '1' && T[i] <= '9');
       
        if (T[i+1] >= '0' && T[i+1] <= '9')  /* 当前行号是一个两位数,例如'10D' */
        {
            // 确保不越界,下一位必须是字母
            assert(i < (LEN_T-2));
            assert(T[i+2] >= 'A' && T[i+2] <= 'Z');
           
            x = (T[i] - '0')*10 + T[i+1] - '1';   // 行号下标
            y = T[i+2] - 'A';                     // 列号下标
            i += 4;                               // 此种情况步长为4
        }
        else  /* 当前行号是个一位数 */
        {
            // 下一位必须是字母
            assert(T[i+1] >= 'A' && T[i+1] <= 'Z');
           
            x = T[i] - '1';        // 行号下标
            y = T[i+1] - 'A';      // 列号下标
            i += 3;                // 此种情况步长为3
        }
       
        // 检查参数有效性和重复参数
        if (x >= N || y >= N)
        {
            std::cout << "\nERROR: Seat NO. out-of-range in third argument ["
                      << T << "](" << (x+1) << char('A'+y) << ")!\n";
            return -1;
        }
        if (wood_boat[x*N+y] != '.')
        {
            std::cout << "\nERROR: Duplicate seat NO. in third argument ["
                      << T << "](" << (x+1) << char('A'+y) << ")!\n";
            return -1;
        }
       
        // 标记木筏中渔民的位置'T'
        wood_boat[x*N+y] = 'T';
       
        // 判断当前渔民位于哪个象限,并且分别计数
        if      (x < H  && y <  H)   ++T1;
        else if (x < H  && y >= H)   ++T2;
        else if (x >= H && y <  H)   ++T3;
        else  /*(x >= H && y >= H)*/ ++T4;
    }// end while
   
    // Step-3: 算出四个象限空位的数量
    E1 = Q - S1 - T1;
    E2 = Q - S2 - T2;
    E3 = Q - S3 - T3;
    E4 = Q - S4 - T4;
   
    // Step-4: 打印木筏的布局
    std::cout << "\n  ┌----┬----┐";
    std::cout << "\n  │ Q1 │ Q2 │";
    std::cout << "\n  ├----┼----┤";
    std::cout << "\n  │ Q3 │ Q4 │";
    std::cout << "\n  └----┴----┘\n";
   
    std::cout << "\nIn this case, \n";
    std::cout << "1th Quadrant(Q1): [S:" << S1 << ", T:" << T1 << ", E:" << E1 << "]\n";
    std::cout << "2th Quadrant(Q2): [S:" << S2 << ", T:" << T2 << ", E:" << E2 << "]\n";
    std::cout << "3rd Quadrant(Q3): [S:" << S3 << ", T:" << T3 << ", E:" << E3 << "]\n";
    std::cout << "4th Quadrant(Q4): [S:" << S4 << ", T:" << T4 << ", E:" << E4 << "]\n";
   
    // 打印木筏矩阵图像
    printf("\n    ");
    for (i = 0; i < N; ++i)
        printf("%c ", 'A'+i);
    printf("\n");
    for (i = 0; i < N; ++i)
    {
        printf("%2d[ ", i+1);
        for (j = 0; j < N; ++j)
        {
            printf("%c ", wood_boat[i*N+j]);
        }
        printf("]\n");
    }
    printf("\n");
    delete []wood_boat;
   
    // Step-5: 判断平衡性并计算最大可坐员工的数量
    // Note: 任何一个象限都有可能不坐一个员工就已经平衡了,但未必就没有解;
    //       或者它的所有空位都坐满员工了才能平衡。所以应从0到Ei全部遍历。
    //       在任何一个象限里,如果坐不满就能满足平衡要求了,那么人的具体
    //       位置并不重要。所以,我们只需要知道每个象限最多能坐多少员工并且
    //       还能够保持平衡即可。
    int result = 0;
    for (i = 0; i <= E1; ++i)
        for (j = 0; j <= E2; ++j)
            for (m = 0; m <= E3; ++m)
                for (n = 0; n <= E4; ++n)
                {
                    // 每个象限可坐人的数量(渔民和员工,空桶不计重量)
                    int C1 = i + T1, C2 = j + T2, C3 = m + T3, C4 = n + T4;
                    // 平衡否?
                    if (((C1+C2) == (C3+C4)) && ((C1+C3) == (C2+C4)))
                    {
                        // 当平衡时可坐的员工数为一个解
                        const int total = i + j + m + n;
                       
                        // 平衡后排除全0组合(原有渔民已经平衡)(而不能一开始就排除全0组合)
                        if (total == 0) continue;
                       
                        std::cout << "Found solution [Employe: " << total << "]! Layout: [Q1:" << i
                                  << "],[Q2:" << j << "],[Q3:" << m << "],[Q4:" << n << "]\n";
                       
                        // 是否为最大解?
                        if (total > result)
                        {
                            std::cout << "*** (And this is a bigger result than " << result << ")\n";
                            result = total;
                        }
                        else
                        {
                            std::cout << "*** (Already got a same/bigger result: " << result << ")\n";
                        }
                    }// end if
                }// end for
   
    // Step-6: 是否有解?
    if (result == 0)
        return -1;
    else
        return result;
}

// please don't modify main function
// Input parameter format: "4 [1B,1C,4B,1D,2A] [3B,2D]"
int main( int argc, const char* argv[] )
{
      if (argc != 4)
    {
        printf("\nDon't use whitespace or other special char in all arguments!\n\n");
        return 1;
    }
   
    // 断言N是偶数,而且N的取值范围为[2,26],即可能为两位数
    int N = atoi(argv[1]);
    assert(N >=2 && N <= 26 && N == (N/2)*2);
   
    int len = strlen(argv[2]);
    assert(len >= 2);
    char* str1 = new char[len-2+1];
    strncpy(str1, argv[2]+1, len-2);       // remove '[' and ']' from two sides
    str1[len-2] = '\0';                    // append '\0'
   
    len = strlen(argv[3]);
    assert(len >= 2);
    char* str2 = new char[len-2+1];
    strncpy(str2, argv[3]+1, len-2);       // remove '[' and ']' from two sides
    str2[len-2] = '\0';                    // append '\0'
   
    printf("\nAt last, Result: %d\n\n", solution(N, str1, str2));
   
    delete []str2;
    delete []str1;
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值