有些公司需要你先答题,后面试,比如这家公司,主要做海外游戏的,所以出题难度也确实很大,请看下面
<?php
/**
* 题目说明及要求:
* - 请使用面向对象思维答题,且不要依赖枚举
* - 禁止出现notice,请自行修改php.ini,不要屏蔽错误输出
* - 请不要修改代码结构,getAllCardNumSet与sortCards的确是独立的function,不是类函数,不要挪到Card类里
* 扑克牌编码规则如下:
* - 使用从1开始的十进制整数为每张扑克牌进行编码
* - 花色顺序:黑桃,红桃,梅花,方块
* - 每副牌占用64个数字,如1-64,65-128,以此类推;每个花色占用16个数字,但实际使用了13个,如1-13是黑桃A到K,17-29是红桃A到K,以此类推;最后两个数字为大小王,如63,64
* - 通配牌:大王、小王、所有牌值是2的牌,可以作为任意花色任意牌值
*
* 请根据提示完善以下功能
*/
class Card
{
/**
* @param int $cardNum 即扑克牌编码,如17 29 63 等
*/
public function __construct(int $cardNum)
{
//todo code
}
}
/**
* 示例:
* 输入:1,false;输出:array_merge(range(1,13), range(17,29), range(33,45), range(49,61))
* @param int $n 几副牌 大于0的正整数
* @param bool $useJoker 是否有王
* @return array 根据参数返回n副牌的全部编码
*/
function getAllCardNumSet(int $n, bool $useJoker): array
{
//todo code
return [];
}
/**
* 说明:
* 输入可能有任意花色,需要返回最长的花色的结果,如最长长度的花色有多个,则随机返回一个
* 相同花色的排序规则:按照牌值(即A-K)从小到大排序,排序规则分为三种,A(1),通配牌,其他牌
* - A(1):如果只有一张A作为1处理,如果大于1张,将其中的一张作为K的下一张牌处理,即排序规则为:A23456789,10,J,Q,K,A
* - 通配牌:
* 只能充当不存在的那张牌(下称缺口),同一缺口只能使用一张通配牌,比如已有135,则缺口为2467...等,且不能有两张通配牌同时作为2(由于所有2是通配牌,所以2这个位置必然是缺口)
* 如已有的通配牌数量不足以插入所有缺口,则忽略剩余缺口,只要保证从小到大的顺序即可,如大于缺口数量,则舍弃剩余通配牌
* 优先使用同花色通配牌插入缺口,优先使用同花色2插入2这个缺口(非必须要求,加分项)
* - 其他牌:如果有多张一样牌值的如3,则排在一起,并且这两张牌排序不分前后
* 示例:
* 输入:[3,2,1,66,65,67,17,18,10,11,19,36,53] 输出:[1,2,3,67,66,18,10,11,65]
*
* @param array $input 不重复整数的一维数组,即getAllCardNumSet()的子集
* @return array 返回最长同花色排序结果的一维数组
*/
function sortCards(array $input): array
{
//todo code
return [];
}
$all = getAllCardNumSet(2, true);
var_dump($all);
shuffle($all);
var_dump(sortCards(array_slice($all, 0, rand(10, 14))));
答案->上代码:
<?php
/**
* 题目说明及要求:
* - 请使用面向对象思维答题,且不要依赖枚举
* - 禁止出现notice,请自行修改php.ini,不要屏蔽错误输出
* - 请不要修改代码结构,getAllCardNumSet与sortCards的确是独立的function,不是类函数,不要挪到Card类里
* 扑克牌编码规则如下:
* - 使用从1开始的十进制整数为每张扑克牌进行编码
* - 花色顺序:黑桃,红桃,梅花,方块
* - 每副牌占用64个数字,如1-64,65-128,以此类推;每个花色占用16个数字,但实际使用了13个,如1-13是黑桃A到K,17-29是红桃A到K,以此类推;最后两个数字为大小王,如63,64
* - 通配牌:大王、小王、所有牌值是2的牌,可以作为任意花色任意牌值
*
* 请根据提示完善以下功能
*/
class Card
{
public int $cardNum;
/**
* @param int $cardNum 即扑克牌编码,如17 29 63 等
*/
public function __construct(int $cardNum)
{
$this->cardNum = $cardNum;
}
/**
* 返回当前扑克是否为通配牌
*
* @return bool
*/
function isWildcard(): bool
{
return in_array($this->getIndex(), [2, 63, 64]);
}
/**
* 返回当前扑克的花色
*
* @return int 1黑桃,2红桃,3梅花,4方块,0错误
*/
function getColor(): int
{
$n = floor(($this->cardNum - 1) / 64);
$cardNum = $this->cardNum - $n * 64;
for ($color = 1; $color <= 4; $color++) { // 四个花色
if ($color * 16 > $cardNum) {
return $color;
}
}
return 0;
}
/**
* 返回当前扑克的大小王
*
* @return int 1小王,2大王,0不是大小王,
*/
function getKing(): int
{
$n = floor(($this->cardNum - 1) / 64);
$index = $this->cardNum - $n * 64;
if ($index == 63) {
return 1;
}
if ($index == 64) {
return 2;
}
return 0;
}
/**
* 返回当前扑克的的索引
*
* @return int A~K对应的1~13
*/
function getIndex(): int
{
$n = floor(($this->cardNum - 1) / 64);
$index = $this->cardNum - $n * 64;
$n = floor(($index - 1) / 16);
return $index - $n * 16;
}
function __toString(): string
{
static $kings = ['?', '?'];
static $colors = ['♠️', '♥️', '♣️', '♦️'];
static $indexs = ['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K'];
$king = $this->getKing();
if ($king) {
return $kings[$king - 1];
}
$color = $this->getColor();
$index = $this->getIndex();
return $colors[$color - 1] . $indexs[$index - 1];
}
function __debugInfo(): array
{
return [$this->cardNum => (string)$this];
}
}
// 断言测试
assert(new Card(1) == '♠️A');
assert(new Card(2) == '♠️2');
assert(new Card(63) == '?');
assert(new Card(64) == '?');
assert(new Card(17) == '♥️A');
assert(new Card(18) == '♥️2');
/**
* 示例:
* 输入:1,false;输出:array_merge(range(1,13), range(17,29), range(33,45), range(49,61))
* @param int $n 几副牌 大于0的正整数
* @param bool $useJoker 是否有王
* @return array 根据参数返回n副牌的全部编码
*/
function getAllCardNumSet(int $n, bool $useJoker): array
{
$set = [];
for ($num = 1; $num <= $n; $num++) { // 几副牌
for ($color = 1; $color <= 4; $color++) { // 四个花色
$offset = ($color - 1) * 16 + ($num - 1) * 64;
$set[] = range($offset + 1, $offset + 13); // A~K
}
if ($useJoker) {
$offset = ($num - 1) * 64;
$set[] = [$offset + 63, $offset + 64]; // 大小王
}
}
$set = array_merge(...$set); // 合并数组
return $set;
}
/**
* 说明:
* 输入可能有任意花色,需要返回最长的花色的结果,如最长长度的花色有多个,则随机返回一个
* 相同花色的排序规则:按照牌值(即A-K)从小到大排序,排序规则分为三种,A(1),通配牌,其他牌
* - A(1):如果只有一张A作为1处理,如果大于1张,将其中的一张作为K的下一张牌处理,即排序规则为:A23456789,10,J,Q,K,A
* - 通配牌:
* 只能充当不存在的那张牌(下称缺口),同一缺口只能使用一张通配牌,比如已有135,则缺口为2467...等,且不能有两张通配牌同时作为2(由于所有2是通配牌,所以2这个位置必然是缺口)
* 如已有的通配牌数量不足以插入所有缺口,则忽略剩余缺口,只要保证从小到大的顺序即可,如大于缺口数量,则舍弃剩余通配牌
* 优先使用同花色通配牌插入缺口,优先使用同花色2插入2这个缺口(非必须要求,加分项)
* - 其他牌:如果有多张一样牌值的如3,则排在一起,并且这两张牌排序不分前后
* 示例:
* 输入:[3,2,1,66,65,67,17,18,10,11,19,36,53] 输出:[1,2,3,67,66,18,10,11,65]
*
* @param array $input 不重复整数的一维数组,即getAllCardNumSet()的子集
* @return array 返回最长同花色排序结果的一维数组
*/
function sortCards(array $input): array
{
/** @var Card[] $wildcards 所有的通配牌 */
$wildcards = [];
/** @var Card[][] $cards 所有的同花牌(不含通配牌) */
$cards = [];
foreach ($input as $k => $v) {
$card = new Card($v);
if ($card->isWildcard()) {
$wildcards[] = $card;
} else {
$cards[$card->getColor()][] = $card;
}
}
// 同花长度排序 将长度从大到小排序
uasort($cards, fn ($a, $b) => count($b) <=> count($a));
// 通配从小到大
usort($wildcards, fn ($a, $b) => $a->getIndex() <=> $b->getIndex());
// 当前花色(长度最大的花色)
$color = array_key_first($cards);
if ($color) {
// 将与当前花色相同的牌放前面
usort($wildcards, function ($a, $b) use ($color) {
if ($a->getColor() == $color) {
return -1;
}
if ($b->getColor() == $color) {
return 1;
}
return 0;
});
}
/** @var Card[] 最长的同花牌 */
$lists = $cards[$color] ?? [];
// 从小到大排序
usort($lists, fn ($a, $b) => $a->getIndex() <=> $b->getIndex());
// A(1):如果只有一张A作为1处理,如果大于1张,将其中的一张作为K的下一张牌处理,即排序规则为:A23456789,10,J,Q,K,A
$a = [];
foreach ($lists as $k => $card) {
if ($card->getIndex() == 1) {//把所有的A放a里
$a[$k] = $card;
}
}
if (count($a) > 1) {
unset($a[array_key_first($a)]);//删掉存在的第一个a
foreach ($a as $k => $card) {//处理剩下的a,并将其放数组最后
unset($lists[$k]);
$lists[] = $card;
}
}
$lists = array_values($lists);
// 通配牌:
// 只能充当不存在的那张牌(下称缺口),同一缺口只能使用一张通配牌,比如已有135,则缺口为2467...等,且不能有两张通配牌同时作为2(由于所有2是通配牌,所以2这个位置必然是缺口)
// 如已有的通配牌数量不足以插入所有缺口,则忽略剩余缺口,只要保证从小到大的顺序即可,如大于缺口数量,则舍弃剩余通配牌
// 优先使用同花色通配牌插入缺口,优先使用同花色2插入2这个缺口(非必须要求,加分项)
// 其他牌:如果有多张一样牌值的如3,则排在一起,并且这两张牌排序不分前后
$inserts = [];
foreach ($lists as $i => $current) {
if (!$wildcards) {
break;
}
$prev = $lists[$i - 1] ?? null;
if ($prev === null) {
$diff = $current->getIndex() - 1;//第一张牌时
} else {
$diff = $current->getIndex() - $prev->getIndex() - 1;
}
if ($diff < 1) {
continue;
}
// 找到lists需要插入通配牌的位置,并把$wildcards分割到找到的位置处(key为位置)
$inserts[$i] = array_splice($wildcards, 0, $diff);
}
krsort($inserts);
foreach ($inserts as $pos => $_wildcards) {//从大到小插入统配牌
array_splice($lists, $pos, 0, $_wildcards);
}
foreach ($wildcards as $card) {//剩余的统配牌
$lists[] = $card;
}
// 还原为数字
$new = [];
foreach ($lists as $card) {
$new[] = $card->cardNum;
}
return $new;
}
echo '<pre>';
// 输入:[3,2,1,66,65,67,17,18,10,11,19,36,53] 输出:[1,2,3,67,66,18,10,11,65]
var_dump(implode(',', sortCards([3, 2, 1, 66, 65, 67, 17, 18, 10, 11, 19, 36, 53])));
// $r = [1, 2, 3, 67, 66, 18, 10, 11, 65];
// foreach ($r as $k => $v) {
// $r[$k] = new Card($v);
// }
// var_dump($r);
// $all = getAllCardNumSet(2, true);
// var_dump($all);
//
// shuffle($all);
//
// var_dump(sortCards(array_slice($all, 0, rand(10, 14))));