问题描述
我正在尝试在 3 个玩家之间获得所有可能的 n
卡交易,其中每个玩家 i
需要正好 n_i
卡,而一些玩家无法获得一些卡片。
例如,假设有4张牌:Ac,Jh,9d,2s
和玩家N,E,S
分别需要1,1,2
张牌,但是N
不能得到Ac
,并且 S
无法获得 Jh
。
输入是n
卡片的列表,以及每个位置和每张卡片的限制:
List<Card> unplayedCards
Map<Position,Integer> numberOfCardsEachPlayerNeeds
Map<Card,List<Position>> possiblePositionsForCard
输出应该是可能的交易列表
List<Map<Position,List<Card>>>
我用 Java 编写,但答案是哪种语言并不重要。
解决方法
使用递归算法最容易做到这一点。类似Python的伪代码:
function possible_deals(players,cards):
output := []
generate_deals(players,cards,{},output)
return output
function generate_deals(players,index,assignment,output):
if index >= len(cards):
output.append(assignment)
else:
card := cards[index]
for player in players:
if player doesn't have enough cards yet and player can have card:
assignment[card] = player
generate_deals(players,index + 1,output)
del assignment[card]
我们为 assignment
和 output
假设传递引用语义,以便递归函数调用可以修改它们。
output
列表中的每个条目都包含一张从卡片到玩家的地图。由此,很容易重建手部。
根据@Thomas 的回答制作了一个 Java 版本。
static List<Map<Position,CardsList>> getAllPossibleDeals(CardsList cards,Map<Position,Integer> cardsPerPosition,Map<Card,List<Position>> positionsPerCard,List<Card> unplayedCardsOrderedByPossibilities) {
List<Map<Position,CardsList>> possibleDeals = new ArrayList(); // all the deals
Map<Position,CardsList> assignment = new HashMap<>(); // a single deal.
generateDeals(cards,cardsPerPosition,positionsPerCard,possibleDeals,assignment);
return possibleDeals;
}
private static void generateDeals(CardsList cards,List<Map<Position,CardsList>> possibleDeals,int i,CardsList> assignment) {
if (i >= cards.size()){
Map<Position,CardsList> assignmentCopy = deepCopyMap(assignment);
possibleDeals.add(assignmentCopy);
}
else {
Card card = cards.get(i);
for (Position pos : positionsPerCard.get(card)) {
if (assignment.get(pos) == null || cardsPerPosition.get(pos) > assignment.get(pos).size()){
// position can have the card
assignment.computeIfAbsent(pos,k -> new CardsList()).add(card);
generateDeals(cards,i+1,assignment);
assignment.get(pos).remove(card);
}
}
}
}