灭鼠先锋

求解

本题是一道深度搜索+博弈论问题,当然之前没接触过博弈论也能用逻辑推导做。

首先解读题目,什么样的局面才能胜利。

可以发现,当下到只剩下一个圆圈的时候,我方一定胜。

那什么样的局面才会失败呢?就是对方让我方面对只剩下一个圆圈的情况。

从最终局面剩下一个圆圈的情况考虑,逐步想到最初的情况。使用深度优先搜索,遍历所有路径。如果只剩下一个圆圈时为必输局面,return False,则它的上一层必胜,return True,可以想到再上一层return还是False,下一层还是True,一直到最初的情况为止。由此可以写出dfs函数。

那怎么判定谁胜呢?

根据csdn其他题解,如

(2条消息) 2022年蓝桥杯省赛 C/C++ A组题解_蓝桥杯灭鼠先锋_傅志凌的博客-CSDN博客

,都默认当第一层dfs返回False时,先手胜利。但这种判定方式存在着反例:假设构造”OOOXXXXX“,返回的结果是错误的。

事实上,这个判断语句可以转化为,对于两行四列的所有特定的棋盘,如果先手胜,最优情况进行落子的操作次数一定是奇数,后手胜进行落子的操作次数一定是偶数。而事实并非如此,因此产生了矛盾。

归_溯的结论

这位佬给出了一些破解本题全面的结论。

import java.util.Scanner;
// 1:无需package
// 2: 类名必须Main, 不可修改

public class Main {
    public static void main(String[] args) {
      //结论一,一行排满,下一行开头的必输
      //结论二,剩余格子数为4时,此时有一对22分布格子或者没有,开头填1(这个1必须破坏22分布格子)的必赢
      //结论三,剩余格子必须有两对22分布的格子,开头必输(由结论一和二得)
      //结论四,剩余格子数为5时,如果有2对22分布格子,开头填1(这个1必须不能破坏2对22分布格子)的必赢(由结论三得)
      //结论五,剩余格子数为5时,只有一对22分布的格子,开头填两个的必赢(由结论二得)
      //结论六,剩余格子数为6时,如果有2对22分布格子,开头无论填什么都必输(由结论二,三,四,五得)
      //结论七,剩余格子数为7时,如果有3对22分布格子,开头填两个格子的必输(由结论四和五得)
      //总结,如果开头那个人填一个,第二步必须填一个(任意位置),结合以上所有结论,开头必输,
      //若想先手赢,则一开始必须填两个,并且剩余6个格子符合结论六。
      System.out.print("LLLV");
    }
}

过样例的python代码:

def dfs(s):
    # 当前为必输局势
    if str(s).count("O")==1:
        return False

    # 放一个棋子
    for i in range(len(s)):
        if s[i]=='O':
            temp=s
            temp=list(temp)
            temp[i]='X'
            # 如果下一个局势可以是必输局势,当前局势必胜
            if not dfs(temp):
                return True

    # 放两个棋子
    for i in range(1,len(s)):
            if s[i-1]=='O' and s[i]=='O' and i!=4:
                temp=s
                temp=list(temp)
                temp[i-1]='X'
                temp[i]='X'
                if not dfs(temp):
                    return True

    # 如果下一个局势是必胜局势,当前局势必输
    return False
    
st=['XOOOOOOO','XXOOOOOO' ,'OXOOOOOO' ,'OXXOOOOO']
for i in range(len(st)):
    print('V',end='') if not dfs(st[i]) else print('L',end='')
    print(dfs(st[i]))
    # 如果dfs返回False,则先手胜。

小蓝与钥匙

求解

有两个解法。

第一个解法,不仅必须知道第一类斯特林数s(n,k),表示将 n 个两两不同的元素,划分为 k 个互不区分的非空轮换的方案数的概念,而且因为它没有通项公式,所以还要知道将14个两两不同的元素,划分为14个非空轮换时的s,把它乘C28取14就是结果。

斯特林数 - OI Wiki (oi-wiki.org)

上网址中有计算递推式,可用程序算。

第二个解法,根据实际意义算s(14,14),也就是假设这14个孩子都拿到自己房间的钥匙,然后互相交换,求有多少种交换方法。所以用dfs求全排列+剪枝,时间复杂度O((n-1)!)。

a=[i for i in range(14)]
b=[0]*14
vis=[False]*14
cnt=0

def dfs(s,t):
    global cnt
    if cnt>1286583532342313400:
        print("wrong")
    # 统计有效方案数
    if s==t:
        cnt+=1
    else:
        for i in range(t):
            if s==a[i]:
                continue
            if vis[i]==False:
                vis[i]=True
                b[s]=a[i]
                dfs(s+1,t)
                vis[i]=False
dfs(0,14)
print(cnt)

占个坑

posted on 2023-03-24 23:46  快乐的乙炔  阅读(0)  评论(0)    收藏  举报  来源