算法:居民楼路灯问题

题目描述

给定一个字符串str,只由‘X’和‘.’两中国字符构成。

‘X’表示墙,不能放灯,也不需要点亮,‘.’表示居民点,可以放灯,需要点亮。

如果灯放在i位置,可以让i-1,i和i+1三个位置被点亮,返回如果点亮str中所需要点亮的位置,至少需要几盏灯

题目解析

暴力

int process(std::string str, int index, std::set<int> lights){
    // index来到结束位置的时候,当前方案准备结束
    if(index == str.length()){
        // 检查当前方案能否把所有居民楼都照亮
        for (int i = 0; i < str.length(); ++i) {
            // 当前位置是点的话
            if (str[i] != 'X') {
                if (!lights.count(i - 1)
                    && !lights.count(i)
                    && !lights.count(i + 1)) {
                    return INT32_MAX;
                }
            }
        }
        // 经过for循环的检查,任意点的位置都被照亮了,返回当前有效的一种解
        return lights.size();
    }else{   // str还没结束
        // 选择不放灯
        int no = process(str, index + 1, lights);
        // 只有在i位置是.的时候,才可以选择放灯
        int yes = INT32_MAX;
        if(str[index] == '.'){
            lights.insert(index);
            yes = process(str, index + 1, lights);
            lights.erase(index);
        }
        return std::min(no, yes);
    }
}

// 纯暴力,用来做对数器。点的位置放灯和不放灯全排列
int minLight(std::string str){
    if(str.empty()){
        return 0;
    }
    return process(str, 0, std::set<int>());
}

贪心

在这里插入图片描述

 

int minLight(std::string str){
    int i = 0;            // i从0出发
    int light = 0;        // 当前灯的个数
    while (i < str.length()){
        if(str[i] == 'X'){    // 当前i位置是X,直接跳到下一个位置做决定
            i++;
        }else{
            light++;  // i 位置是 . 不管i+1是X还是.当前区域需要放灯
            if(i + 1 == str.length()){     //当前是最后一个位置了
                break;  
            }else{
                if(str[i + 1] == 'X'){  // 如果i+1位置是X,在i位置放灯,去i+2位置做决定
                    i = i + 2;  
                }else{    // i位置是. i+1也是. 那么不管i+2是什么,都在i+1位置放灯,到i+3去做决定
                    i = i + 3;
                }
            }
        }
    }
    return light;
}

更简洁的解法

// 两个X之间,数一下.的数量,然后除以3,向上取整
// 把灯数累加
int minLight1(std::string str){
    int cur = 0;
    int light = 0;
    for (char c : str) {
        if (c == 'X') {
            light += (cur + 2) / 3;
            cur = 0;
        } else {
            cur++;
        }
    }
    light += (cur + 2) / 3;
    return light;
}

对数器


std::default_random_engine e;
std::string  randomString(int maxLen){
    std::uniform_int_distribution<int> distS(0, maxLen);
    std::uniform_real_distribution<double > dr;
    int size = distS(e);
    std::string ans(size, ' ');
    for (int i = 0; i < size; ++i) {
        ans[i] = dr(e) > 0.5 ? 'X' : '.';
    }
    return ans;
}



int main() {
    int len = 20;
    int testTime = 1000;
    for (int i = 0; i < testTime; i++) {
        std::string test = randomString(len);
        int ans1 = minLight(test);
        int ans2 = minLight1(test);
        if (ans1 != ans2) {
            printf("oops!");
            return -1;
        }
    }
    printf("finish!");
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值