[LeetCode] Can I Win 我能赢吗

In the "100 game," two players take turns adding, to a running total, any integer from 1..10. The player who first causes the running total to reach or exceed 100 wins.

What if we change the game so that players cannot re-use integers?

For example, two players might take turns drawing from a common pool of numbers of 1..15 without replacement until they reach a total >= 100.

Given an integer maxChoosableInteger and another integer desiredTotal, determine if the first player to move can force a win, assuming both players play optimally.

You can always assume that maxChoosableInteger will not be larger than 20 and desiredTotal will not be larger than 300.

Example

Input:
maxChoosableInteger = 10
desiredTotal = 11

Output:
false

Explanation:
No matter which integer the first player choose, the first player will lose.
The first player can choose an integer from 1 up to 10.
If the first player choose 1, the second player can only choose integers from 2 up to 10.
The second player will win by choosing 10 and get a total = 11, which is >= desiredTotal.
Same with other integers chosen by the first player, the second player will always win.

这道题给了我们一堆数字,然后两个人,每人每次选一个数字,看数字总数谁先到给定值,有点像之前那道Nim Game,但是比那题难度大。我刚开始想肯定说用递归啊,结果写完发现TLE了,后来发现我们必须要优化效率,使用哈希表来记录已经计算过的结果。我们首先来看如果给定的数字范围大于等于目标值的话,直接返回true。如果给定的数字总和小于目标值的话,说明谁也没法赢,返回false。然后我们进入递归函数,首先我们查找当前情况是否在哈希表中存在,有的话直接返回即可。我们使用一个整型数按位来记录数组中的某个数字是否使用过,我们遍历所有数字,将该数字对应的mask算出来,如果其和used相与为0的话,说明该数字没有使用过,我们看如果此时的目标值小于等于当前数字,说明已经赢了,或者我们调用递归函数,如果返回false,说明也是第一个人赢了。为啥呢,因为当前我们已经选过数字了,此时就该对第二个人调用递归函数,只有他的结果是false,我们才能赢,所以此时我们标记true,返回true。如果遍历完所有数字,我们标记false,返回false,参见代码如下:

class Solution {
public:
    bool canIWin(int maxChoosableInteger, int desiredTotal) {
        if (maxChoosableInteger >= desiredTotal) return true;
        if (maxChoosableInteger * (maxChoosableInteger + 1) / 2 < desiredTotal) return false;
        unordered_map<int, bool> m;
        return canWin(maxChoosableInteger, desiredTotal, 0, m);
    }
    bool canWin(int length, int total, int used, unordered_map<int, bool>& m) {
        if (m.count(used)) return m[used];
        for (int i = 0; i < length; ++i) {
            int cur = (1 << i);
            if ((cur & used) == 0) {
                if (total <= i + 1 || !canWin(length, total - (i + 1), cur | used, m)) {
                    m[used] = true;
                    return true;
                }
            }
        }
        m[used] = false;
        return false;
    }
};

类似题目:

Nim Game

参考资料:

https://discuss.leetcode.com/topic/68896/java-solution-using-hashmap-with-detailed-explanation/2

LeetCode All in One 题目讲解汇总(持续更新中...)

时间: 2024-12-04 16:08:44

[LeetCode] Can I Win 我能赢吗的相关文章

python 练习 2

#!/usr/bin/python # -*- coding: utf-8 -*- from random import shuffle class caigame: win=False flag=False life=12 what=-1 s1='' map1=[] thenum=0 def start(self): t=range(1,10) shuffle(t) self.thenum=t[0]*1000+t[1]*100+t[2]*10+t[3] self.inmap() print s

JAVA小项目之五子棋

五子棋V1.0 功能: 人人对战,人机对战(初级) 记录双方分数: 主要知识点: 二维坐标系中,各方向坐标的关系及规律. 效果图: "传不了图片?" 主框架类: 1 package com.gxlee.wzq; 2 3 /** 4 *五子棋 Java版 V1.0 5 *@author http://www.cnblogs.com/HFLY 6 *时间2005-8-20 7 *功能:人机对战 人人对战 8 */ 9 import java.awt.Color; 10 import jav

星际争霸II 战斗问题

试题描述 两只跳虫在和陆战队员进行战斗.最初,陆战队员在跳虫离他一定距离时发现了跳虫(两只跳虫并排进攻),开始攻击(只能攻击一只).当跳虫与陆战队员距离为0时,同时发动攻击.假设跳虫一个时间单位走一米,或每只攻击一次.陆战队员每个时间单位攻击一次.问最后是陆战队员赢了,还是跳虫赢.                  VS      输入 五个整数,n(n <= 87) , k(5 <= k <= 8), l(5 <= l <= 8), m(1 <= m <= 35

重温C++---骰子游戏---ShinePans

//this is a program witch played by two people //二人游戏,若第一个抛骰子,抛两次的和为7或11则第一人直接胜利,第二人直接失败 //若第一个人抛骰子,抛两次的和为2,3或12,则第一个人直接失败,第二人胜利 //若第一个人抛筛子,抛两次的和以上均不是,则由第二个人抛,直到有一方胜利 #include <iostream> #include <stdlib.h> #include <ctime> //获取系统时间的头文件

七个习惯的简要定义与架构图

习惯一:积极主动(BE PROACTIVE) 主动积极即采取主动,为自己过去.现在及未来的行为负责,并依据原则及价值观,而非情绪或外在环境来下决定.主动积极的人是改变的催生者,他们扬弃被动的受害者角色,不怨怼别人,发挥了人类四项独特的禀赋——自觉.良知.想像力和自主意志,同时以由内而外的方式来创造改变,积极面对一切.他们选择创造自己的生命这也是每个人最基本的决定. 习惯二:以终为始(BEGIN WITH THE END IN MIND) 所有事物都经过两次的创造——先是在脑海里酝酿,其次才是实质

爬取竞彩足球的数据信息

scrapy startproject ZuCai 会自动生成2个zucai文件夹 cd ZuCai cd ZuCai 进入最下面的ZuCai文件夹 scrapy genspider zucai  trade.500.com/jczq/ 开始分析  https://trade.500.com/jczq/ 这个页面 进入页面后,点击F12查看网页代码.通过查找,发现所有的比赛结果全部在 <table class="bet-tb bet-tb-dg">...</table

三子棋分析与实现——C语言

三子棋作为一款经典的小游戏,对于初学C语言的我们如果 可以熟练了解并可以实现其游戏功能,则对于以后更系统的学习C语言有很好帮助.下来我分享一下我在编程时的感悟和代码 首先介绍一下三子棋规则:只要将自己的棋连成一条线(行,列,斜线),即为赢如图: 先根据图和玩法大致有个思路: 1.我们可以将棋盘看做是一个三行三列的二维数组,且每行每列用线隔开以便区分2.分为电脑下棋和玩家下棋(电脑先下和玩家先下)3.判断输赢(玩家赢,电脑赢和平局) 大致思路即为上面分析,下面开始写代码: 创建tset.c,gam

猜拳游戏and用户登录

猜拳游戏win=0lost=0ping=0while True: print('胜:%s 输:%s 平:%s'% (win,lost,ping)) print('欢迎来猜拳\n1.拳头 2.剪刀 3.布 4.退出') import random chooses = ['石头','剪刀','布'] dn = random.choice(chooses) ren = input('请出拳:') if (ren == '1' and dn =='石头') or (ren == '2' and dn =

状态压缩 - LeetCode #464 Can I Win

动态规划是一种top-down求解模式,关键在于分解和求解子问题,然后根据子问题的解不断向上递推,得出最终解 因此dp涉及到保存每个计算过的子问题的解,这样当遇到同样的子问题时就不用继续向下求解而直接可以得到结果.状态压缩就是用来保存子问题的解的,主要思想是把所有可能的状态(子问题)用一个数据结构(通常是整数)统一表示,再用map把每个状态和对应结果关联起来,这样每次求解子问题时先find一下,如果map里面已经有该状态的解就不用再求了:同样每次求解完一个状态的解后也要将其放入map中保存 状态