对称博弈:取硬币问题(C++实现)

问题描述

问题描述
有n个硬币,围成一个圆,两人比赛,每人每次可拿走一个或两个相邻的硬币,最后一个拿硬币的人胜出。现在给出初始硬币的数量,问先取的人会获胜,还是后取的人获胜。

关键/核心

对称博弈
无论怎样取,先手肯定期望自己胜,而后手也期望自己胜,
没有人期望自己败!对每一步决策,各方都是为了自己胜利!

分析问题

当n=1时,先手胜
当n=2时,先手胜
当n=3时,先手败
当n=4时,先手胜
当n=5时,…

发现本质

假设硬币数n>3,当先手取1个或2个硬币时,将剩下n-1个或n-2个硬币,
此时后手取硬币相当于“先手”角色,由于n-1或n-2是确定的数,
此时的“先手”(实际是后手)的胜负将可以确定。

如果你自己推一遍上文“分析问题”的过程,要理解本质很容易

思路

数学归纳法、递归算法、决策树、模拟

解法一:递归算法

#include <iostream>

bool ifWin(int n);

int main()
{
    using namespace std;
    int n;
    cin >> n;

    cout << ifWin(n);
    return 0;
}


bool ifMe = 0;  // 记录每次递归时,取硬币者。递归时,当先手取硬币时,ifMe值为1,否则为0
// 参数n为硬币数,返回1为先手胜
bool ifWin(int n)
{
    ifMe = !ifMe;   // 取硬币者切换

    if (n == 1) return 1;    // 当n=1时,先手胜
    if (n == 2) return 1;    // 当n=2时,先手胜
    if (n == 3) return 0;    // 当n=3时,先手败

    // 当n>3时

    if ((ifWin(n-1)&&ifMe) || (ifWin(n-2)&&ifMe) ||     // 当先手所有的决策中至少有一个可以胜利时,则先手胜利是充分的
        ((ifWin(n-1)==0)&&(ifMe==0)) || ((ifWin(n-2)==0)&&(ifMe==0)))  // 实际上是在判断:是否存在一种胜的决策并且胜者为先手,是否存在一种败的决策并且败者为后手
    {
        ifMe = 0;   // 取硬币者切换为默认值
        return 1;
    }
    else    // 当先手所有的决策都不能胜利时,则先手失败是必然的
    {
        ifMe = 0;   // 取硬币者切换为默认值
        return 0;
    }
}

上面的算法是将整个过程模拟了一遍,用到了递归算法当n大了,速度会很慢,oj可能会报TLE。(最烦TLE,比WA还烦人)

解法二:懒人做法

这种方法不用递归算法,不存在“当n太大时,速度慢”的问题,理解起来也容易。

注意看:

当n=1时,先手胜
当n=2时,先手胜
当n=3时,先手败
当n=4时,先手胜
当n=5时,先手胜
当n=6时,先手败
当n=7时,先手胜
当n=8时,先手胜
当n=9时,先手败
当n=10时,先手胜
当n=11时,先手胜
当n=12时,先手败

其实列出一些连续的结果后,不难归纳出(不严谨的、非数学的,只是简单地发现规律):当n为3的倍数时,先手败,当n不是3的倍数时,先手胜
所以为了不报TLE,下面是“懒人”做法:

#include <iostream>
int main()
{
    using namespace std;
    int n;
    cin >> n;
    cout << bool(n%3);
    return 0;
}

吐槽

第一种解法确实有点复杂,或是说难以理解,但是当你自己动手推一遍不同的n时的结果,很容易理解的! 我当时在研究这种解法时,在递归函数ifWin()中,对n>3时,第一个if分支只写了:

if ((ifWin(n-1)&&ifMe) || (ifWin(n-2)&&ifMe)) {...}

结果测试了半天,结果总达不到预期,让人很头疼——明明就是按游戏过程模拟的啊,怎么回事???!
当我就要放弃这个绝妙的算法(不得不说,不提运行时间的话,确实很巧妙:从原理上解决此问题,我应该是全网首个哈哈哈,留念2022/12/31, 明天就是元旦了2023加油!!!愿2023没bug没烦恼,oj永远不报TLE!) 时,我突然想到,ifWin()返回0,即决策失败时,可能是后手失败啊(ifMe==0),于是我把第一个if分支改为:

if ((ifWin(n-1)&&ifMe) || (ifWin(n-2)&&ifMe) || ((ifWin(n-1)==0)&&(ifMe==0)) || ((ifWin(n-2)==0)&&(ifMe==0))) {...}

运行测试——
功夫不负有心人!

可能有人说了,既然找规律就能解决的问题,为什么要大费周折去搞一个模拟算法呢?况且n太大时速度还很慢。

我想,对于产品的解决方案,确实是这样,但是, 这个模拟算法不只是为了解决这个问题,更是为这一类问题的解决提供了思路,它很好地阐释了问题的本质,让计算机在本质上贴合实际问题、解决实际问题!

芜湖~2023新年快乐!

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 密码学是研究如何保护信息安全的学科,主要包括加密算法和解密算法的设计与实现。C/C++语言是一种被广泛应用于密码学领域的编程语言,其提供了丰富的库函数和数据类型,方便实现各种密码学算法。 在密码学中,加密算法是用于将明文转换为密文的算法,而解密算法则是用于将密文转换回明文的算法。C/C++语言的灵活性和高效性使其成为实现这些算法的理想选择。 在C/C++语言中,可以使用各种库函数来实现密码学算法。例如,可以使用OpenSSL库函数来实现各种对称加密算法(如AES、DES)和非对称加密算法(如RSA、DSA)。这些库函数提供了一系列接口,可以方便地进行密钥生成、加密和解密操作。 此外,C/C++语言还提供了丰富的数据类型和运算操作,可以方便地进行数据处理和运算。例如,可以使用位运算来实现一些常见的密码学操作,如异或操作和循环左移操作。同时,C/C++语言还提供了高效的数据结构,如数组和指针,可以方便地进行数据存储和访问。 总之,C/C++语言在密码学中的应用广泛且灵活。通过使用C/C++语言,可以方便地实现各种加密算法和解密算法,并保护信息的安全。但是在使用C/C++语言实现密码学算法时,需要注意安全性和效率的问题,避免出现漏洞和性能问题。 ### 回答2: 密码学是一门关于保护信息安全的学科,它研究如何设计密码算法以及如何使用密码算法来加密和解密信息。C/C++是一种广泛使用的编程语言,它具有快速、高效的特点,非常适合用来实现密码学算法。 在密码学中,常用的算法对称加密算法和非对称加密算法对称加密算法使用同一个密钥来进行加密和解密,而非对称加密算法使用一对密钥,其中一个用于加密,另一个用于解密。 在C/C++中,我们可以使用库函数来实现密码学算法。例如,在对称加密算法中,可以使用openssl库中的函数来实现AES、DES等算法。这些函数提供了加密和解密的接口,我们只需要调用相应的函数,并传入待加密的明文和密钥参数即可实现加密和解密过程。 对于非对称加密算法,我们可以使用openssl库中的函数来实现RSA算法。这些函数提供了生成密钥对、公钥加密、私钥解密的功能,我们可以使用这些函数来实现基于RSA的加密和解密操作。 除了使用库函数外,我们还可以自己编写密码学算法实现。C/C++具有丰富的数据类型和数据结构,我们可以利用这些特性来实现不同的密码学算法,例如SHA-256哈希算法、AES加密算法等。只需要按照算法的规则进行编码,进行相关的运算和操作即可实现密码学算法。 总之,C/C++语言可以很方便地实现密码学算法,无论是使用库函数还是自己编写算法实现,都可以利用C/C++的特性和功能来保护信息的安全。 ### 回答3: 密码学是一项研究如何保护通信安全和数据保密性的学科。在密码学中,密钥起着至关重要的作用,因为正确的密钥是保护数据的关键。C/C++语言是一种广泛应用于软件开发的编程语言,可以实现各种密码学算法和协议。 在密码学中,常用的算法对称加密算法和非对称加密算法对称加密算法使用相同的密钥进行加密和解密,如AES和DES算法。我们可以使用C/C++语言实现这些算法,通过调用相应的函数来加密和解密数据。这需要理解算法的原理和加密过程,以及相关的库函数和语法。 非对称加密算法使用一对公私钥进行加密和解密,如RSA算法。在C/C++中,可以使用相关库函数来生成密钥对、加密和解密数据。这种加密算法更加安全,但由于复杂性和计算开销较大,通常只用于加密较短的数据或者用于对称密钥的安全传输。 除了加密算法,C/C++语言还可以实现密码学中的其他功能,如哈希函数和数字签名。哈希函数用于将任意长度的数据转化为固定长度的哈希值,常用的算法有MD5和SHA。数字签名用于验证数据的完整性和真实性,通过私钥对数据进行签名,再通过公钥进行验证。 总之,C/C++语言可以通过实现密码学算法和相关功能来保护通信安全和数据保密性。为了确保安全性,需要深入理解密码学的原理和算法设计,同时注意使用安全的库函数和保护密钥的安全存储。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值