程序员的算法趣题Q67: 不挨着坐是一种礼节吗?

目录

1. 问题描述

2. 解题分析

2.1 基本思路

2.2 动态规划

2.3 算法流程

3. 代码及测试

4. 后记


1. 问题描述

        注意,本问题不区分人,只考虑各个座位被占用的不同顺序的个数。 

2. 解题分析

2.1 基本思路

        每个人选座位时,首先看有没有“超空位”(两边都没有挨着人的空位暂称为“超空位”),如果有则优先从“超空位”中选取。如果没有超空位,则从空位中任选一个坐下(空位是一定有的,因为人数和座位数是相等的)。

        由于车厢中的作为安排方式(对面两排),两头两位的座位有一边是一定不挨着人的。如何表达才最方便于实现呢?

        借用棋盘问题中的围栏思想,将两排座位排成一排,但是两头和中间各插入一个dummy座位(不能坐人),这样连同dummy座位总共有15个,可以表达为长度为15的数组。Dummy座位置为”-1”表示不能坐人,正常座位初始化为“0”,被人占据了则复制为“1”。这样,判断一个正常座位是否为“超空位”,只要看它本身是否为0且两边都不是1(即为“0”或者“-1”)。

2.2 动态规划

        考虑已经有k个座位被占了,从当前状态出发往后的剩下的(12-k)个座位被占的顺序数只与当前这个k个座位被占的状态有关,而与它们被占的顺序无关。因此这个可以用动态规划的策略来解决。本题考虑用top-down的方式(递归+memoization)来实现动态规划策略。

2.3 算法流程

        算法流程如下:

3. 代码及测试

# -*- coding: utf-8 -*-
"""
Created on Sun Oct 17 10:52:39 2021

@author: chenxy
"""

import sys
import time
import datetime
import math
# import random
from   typing import List
# from   queue import Queue
# from   collections import deque
import itertools as it
import numpy as np

memo = dict()

def isFull(pos):
    return np.all(pos[1:7]) and np.all(pos[8:14])

def search(pos):
    
    if tuple(pos) in memo:
        return memo[tuple(pos)]
    if isFull(pos):
        return 1
    
    cnt = 0
    hasSuperEmpty = False
    # Search super empty seat
    for k in [1,2,3,4,5,6,8,9,10,11,12,13]:
        if pos[k]==0 and pos[k-1]!=1 and pos[k+1]!=1:
            pos[k] = 1
            cnt = cnt + search(pos)
            hasSuperEmpty = True
            pos[k] = 0
        
    if not hasSuperEmpty:
        for k in [1,2,3,4,5,6,8,9,10,11,12,13]:
            if pos[k]==0:
                pos[k] = 1
                cnt = cnt + search(pos)
                pos[k] = 0
    memo[tuple(pos)] = cnt
    return cnt

N   = 12 # Can't modify currently
pos = np.zeros(N+3)
pos[N//2+1] = -1
pos[N+2]    = -1

tStart = time.perf_counter()
count = search(pos)
tCost  = time.perf_counter() - tStart
print('count = {0}, tCost = {1:6.3f}(sec)'.format(count,tCost))      

        运行结果:count = 14100480, tCost =  0.032(sec) 

4. 后记

        轻松搞定。

        跳过了前面一些题目先做这道,是因为浏览了一下“高级篇”中的题目,大多数题目想2~3分钟感觉没有头绪,先捏这种一看就觉得“顺眼”的软柿子捏一捏^-^。这种策略对于学习很重要,盲目地死磕一道题目花费太久时间容易产生挫折感从而打击学习的积极性。有些问题一时没有头绪,放在脑袋里供闲暇时间想一想说不定更容易突然就有什么灵感。。。

        上一篇:Q66: 设计填字游戏

        下一篇:Q68: 异性相邻的座位安排

        本系列总目录参见:程序员的算法趣题:详细分析和Python全解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

笨牛慢耕

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值