文章目录
1.青草游戏-博弈
分析:
这是一道博弈题,题目背后就是数学思想。我们这里可以不深究内部的数学思想,只通过规律来求解
1.0-5这个情况我们可以简单分析
2.当我们niu吃完了草之后,递归调用这个算法。比如说,现在有6的草,牛牛先吃1,然后剩余5。然后5是yang赢了,所以niu赢了。是不是很烧脑?
3.简单来说,第二轮的先手,是第一轮的后手。所以,在递归进去的时候,就是出现了身份互换。所以在递归里面,yang赢了,就是代表niu赢了。
4.但是这个算法递归深度太深,所以只能对100以内的使用。
using System;
namespace test
{
class test
{
static void Main()
{
int count = Convert.ToInt32(Console.ReadLine());
for (int i = 0; i < count; i++)
{
Console.WriteLine(Win(Convert.ToInt32(Console.ReadLine())));
}
}
static string Win(int n)
{
if (n <= 5)
{
if (n == 2 || n == 5|| n==0 )
{
return "yang";
}
else
{
return "niu";
}
}
int cur = 1;
while (cur <= n)
{
if (Win(n - cur) == "yang")
{
return "niu";
}
if (cur > n / 4) break;
cur *= 4;
}
return "yang";
}
}
}
5.但是,我们可以总结出规律,temp % 5 == 0 || temp % 5 == 2的时候是yang赢,其他情况是niu赢
6.所以,以后玩博弈,记得做先手!!!
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _013
{
class Program
{
static void Winner()
{
int n = Int32.Parse(Console.ReadLine());
long[] arr = new long[n];
for(int i = 0; i < n; i++)
{
arr[i] = Int32.Parse(Console.ReadLine());
}
foreach(long temp in arr)
{
if (temp % 5 == 0 || temp % 5 == 2)
{
Console.WriteLine("yang");
}
else
{
Console.WriteLine("niu");
}
}
}
static void Main(string[] args)
{
Winner();
Console.ReadKey();
}
}
}
2.正方形染色-预处理
分析:
题目的意思是,让左边全部都是R(最少一个),右边全部是G
具体情况呢,并不是说让R左移,G右移
不是说开始有多少个R,结束就得有多少个R
只需要涂最少的色,让仅剩的R在左边,G全部在右边
这里就用到了预处理!
1.什么是预处理?就是提前将我们后面计算需要的东西进行准备
2.在这里,我们就是用分界线的方式,去遍历每个位置
3.首先,我们一个for循环,将left数组进行初始化。这就是我们的预处理。我们left数组,记录的就是当前位置i,左边的R个数。Right记录的是放弃
using System;
namespace test
{
class test
{
static void Main()
{
string arr = Console.ReadLine();
int min = 100000;
int[] left = new int[arr.Length];
int[] right = new int[arr.Length];
//left预处理
for (int i = 0; i < arr.Length; i++)
{
if (arr[i] == 'G')
{
if (i == 0)
{
left[i] = 1;
}
else
{
left[i] = left[i - 1] + 1;
}
}
else
{
if (i == 0)
{
left[i] = 0;
continue;
}
left[i] = left[i - 1];
}
}
//Right预处理
for (int i = arr.Length - 1; i >= 0; i--)
{
if (arr[i] == 'R')
{
if (i == arr.Length - 1)
{
right[i] = 1;
}
else
{
right[i] = right[i + 1] + 1;
}
}
else
{
if (i == arr.Length - 1)
{
right[i] = 0;
continue;
}
right[i] = right[i + 1];
}
}
//遍历索引
for (int i = 0; i < arr.Length; i++)
{
if(i == arr.Length - 1)
{
min = Math.Min(left[i], min);
break;
}
if (i == 0)
{
min = Math.Min(right[i], min);
continue;
}
//最小染色块数,就是将左边的G全部染成R,右边的R全部染成G
min = Math.Min(left[i] + right[i+1], min);
}
Console.WriteLine(min);
}
}
}
3.边框全1最大正方形大小 -预处理
牛客网连接
分析:
首先我们来对可能的正方形个数来进行判断
综上,我们可以知道,最多有N的三次方个正方形
那么,我们的时间复杂度是N的三次方,但是,我们在遍历这N的三次方个正方形的时候,我们还得对其进行判断。即,以a点作为左上方点时,最大的正方形边长是多少?这里就需要遍历,那么总的时间复杂度就是N的四次方了现在
那么,我们可以优化吗?
可以,预处理。
我们提前将当前位置下面和右边所以的1的长度,进行记录。等我们到i位置的时候,我们就可以轻松的知道,我的右边有多少个1,下面有多少个1,能不能组成一个正方形了!
1.我们定义一个和Matrix一样大的R矩阵,专门用来记录i,j位置右边1的个数。然后是D矩阵,专门记录i,j位置下面1的个数
2.我们只需要在几个特殊的位置判断即可。即找出D,R中小的那个,作为边长。然后去边的另外的一个顶点,去判断边的长度是不是也是指定边的长度。最后去判断i+边长,j+边长的位置,是不是1
3.但是有特殊情况,比如说下面。我们就算拿到了R和D中比较小的边,但是这个边也不是我们要找的正方形的边。所以在这里,我们对边长len需要进行逐一判断,比如len一开始是40。那么我们就对39,38,39…一直到长度2进行判断即可。
using System;
namespace test{
class test{
static void Main(){
int len=Convert.ToInt32( Console.ReadLine());
int[,] Matrix=new int[len,len];
int[,] MatrixR=new int[len,len];
int[,] MatrixD=new int[len,len];
for(int i=0;i<len;i++){
string[] getin=Console.ReadLine().Split(' ');
for(int j=0;j<len;j++){
Matrix[i,j]=Convert.ToInt32(getin[j]);
}
}
//初始化R
for(int i=0;i<len;i++){
for(int j=len-1;j>=0;j--){
if(Matrix[i,j]==1){
if(j==len-1){
MatrixR[i,j]=1;
}else{
MatrixR[i,j]=MatrixR[i,j+1]+1;
}
}else{
MatrixR[i,j]=0;
}
}
}
//初始化D
for(int i=0;i<len;i++){
for(int j=len-1;j>=0;j--){
if(Matrix[j,i]==1){
if(j==len-1){
MatrixD[j,i]=1;
}else{
MatrixD[j,i]=MatrixD[j+1,i]+1;
}
}else{
MatrixD[j,i]=0;
}
}
}
int Max=0;
//遍历Matrix
for(int i=0;i<len;i++){
for(int j=0;j<len;j++){
if(Matrix[i,j]==1){
//如果此时R和D都有2以上
if(MatrixR[i,j]>1&&MatrixD[i,j]>1){
int min=Math.Min(MatrixR[i,j], MatrixD[i,j]);
//这里是做长度的遍历
while(min>=2){
if(MatrixR[i+min-1,j]>=min && MatrixD[i,j+min-1] >=min ){
if(Matrix[i+min-1,j+min-1]==1){
Max=Math.Max(min, Max);
break;
}
}else{
min--;
}
}
}
}
}
}
Console.WriteLine(Max);
}
}
}
4.已知先序和中序求后序 -二叉树
牛客网链接
解析:
我们总结得如下规律
1.首先前序遍历的根节点在L1位置,然后这个根节点在中序遍历的中间位置,在后序遍历的结束位置,那么我们可以根据根节点在中序遍历的位置,推出我们子树的长度
2.知道子树长度之后,我们就可以将数组拆成“根节点-左子树-右子树”的模式,
然后我们分别对子树进行递归,即可!
using System;
using System.Collections.Generic;
namespace test
{
class test
{
static Dictionary<int, int> iniDic = new Dictionary<int, int>();
static int len = 0;
static void Main()
{
len = Convert.ToInt32(Console.ReadLine());
int[] pre = new int[len];
int[] ini = new int[len];
int[] pos = new int[len];
string[] prein = Console.ReadLine().Split(' ');
for (int i = 0; i < len; i++)
{
pre[i] = Convert.ToInt32(prein[i]);
}
string[] iniin = Console.ReadLine().Split(' ');
for (int i = 0; i < len; i++)
{
ini[i] = Convert.ToInt32(iniin[i]);
//存到字典里面,方便查找
iniDic.Add(ini[i], i);
}
SetPos(pre, 0, len - 1, ini, 0, len - 1, pos, 0, len - 1);
for (int i = 0; i < len; i++)
{
Console.Write(pos[i] + " ");
}
}
static void SetPos(int[] pre, int L1, int R1, int[] ini, int L2, int R2, int[] pos, int L3, int R3)
{
//如果前序遍历中的 子树长度小于1 即不存在 则返回
if (L1 > R1)
{
return;
}
//找到根节点在中序遍历的位置
int find = iniDic[pre[L1]];
//计算子树节点个数
int Count = find - L2;
//赋值
pos[R3] = pre[L1];
//左树
SetPos(pre, L1 + 1, L1 + Count,
ini, L2, find - 1,
pos, L3, L3 + Count - 1);
//右树
SetPos(pre, L1 + Count + 1, R1,
ini, find + 1, R2,
pos, L3 + Count, R3 - 1);
}
}
}
5.二叉树权值和最大路径 -二叉树左右信息归纳
牛客网链接
分析:
1.我们想要获取最大路径的话,我们只需要获取左子树和右子树的最大路径,将其中较大的加上自己然后返回即可
2.然后左子树又去获取自己的左子树,一直循环往复,递归
using System;
using System.Collections.Generic;
namespace test
{
class test
{
static int len = 0;
static void Main()
{
len = Convert.ToInt32(Console.ReadLine());
int[,] arr = new int[len, 2];
for (int i = 0; i < len; i++)
{
string[] putin = Console.ReadLine().Split(' ');
arr[i, 0] = Convert.ToInt32(putin[0]);
arr[i, 1] = Convert.ToInt32(putin[1]);
}
Console.WriteLine(GetBig(arr, 1));
}
//获取最大路径
static int GetBig(int[,] arr, int index)
{
if (index < 0) return 0;
//左子树中最大路径的值
int leftinfo = GetBig(arr, GetLeft(arr, index));
//右子树中最大路径的值
int rightinfo = GetBig(arr, GetRight(arr, index));
//判断左边大还是右边大
int p1 = 0;
int p2 = 0;
if (leftinfo != 0)
{
//左边有树
p1 = arr[index - 1, 0] + leftinfo;
}
if (rightinfo != 0)
{
//右边有树
p2 += arr[index - 1, 0] + rightinfo;
}
if (leftinfo != 0 && leftinfo != 0)
{
return Math.Max(p1, p2);
}
if (leftinfo == 0 && leftinfo == 0)
{
return arr[index - 1, 0];
}
return leftinfo == 0 ? p1 : p2;
}
//获取左节点
static int GetLeft(int[,] arr, int index)
{
for (int i = 1; i < len; i++)
{
if (arr[i, 1] == index)
{
return i + 1;
}
}
return -1;
}
//获取右节点
static int GetRight(int[,] arr, int index)
{
int count = 0;
for (int i = 1; i < len; i++)
{
if (arr[i, 1] == index)
{
count += 1;
if (count == 2)
{
return i + 1;
}
}
}
return -1;
}
}
}
二叉树递归总结
1.在节点可以向左树和右树要info的设定下,头结点应该如何组织可能性?
2.组织可能性看到底要向左子树和向右子树要什么info
3.左info+右info==总info
这道题就是就是头结点需要向左、右子树要最大路径,然后推出总的最大路径
6.二叉树结构计数
牛客网链接
分析:
1.我们当前节点需要从左右子树获取什么信息?节点数
2.比如说1,左子树有三个,右子树有三个,那么总的情况有多少种?答案是二十五种,5*5=25
3.那么,我们只需要一直枚举下去就行了。比如说,节点数是7,那么子树是6。那么就要0,6 1,5 2,4 3,3 4,2 5,1 6,0 六种情况,我们全部加起来即可!
using System;
namespace test{
class test{
static void Main(){
int Count=Convert.ToInt32( Console.ReadLine());
Console.WriteLine(NumTrees(Count));
}
static long NumTrees(int n){
if(n<2)return 1;
long[] dp=new long[n+1];
//MOD有什么用? 下面有解释
long MOD=1000000007;
dp[0]=1;
for(int i=1;i<n+1;i++){
//计算节点个数为i的时候的dp值
//需要将左子树长度从0到i情况都枚举一次
for(int j=1;j<i+1;j++){
//左子树个数的dp*右子树个数的dp
dp[i]+=dp[j-1]*dp[i-j];
//防止溢出int32位长度
dp[i]%=MOD;
}
}
return dp[n];
}
}
}
对1000000007取模?
大数阶乘,大数的排列组合等,一般都要求将输出结果对1000000007取模
为什么总是1000000007呢?
1、大数求余原因:大数越界
大数越界:随着n增大,f(n)会超过Int32甚至Int64的取值范围,导致最终的返回值错误。
当一个问题只对答案的正确性有要求,而不在乎答案的数值,可能会需要将取值很大的数通过求余变小。
2.int32位取值范围是-2147483648~2147483647,1000000007 是最小的十位质数。模1000000007,可以保证值永远在int的范围内。
3.int64位的最大值为2^63-1,对于1000000007来说它的平方不会在int64中溢出
所以在大数相乘的时候,因为(a∗b)%c=((a%c)∗(b%c))%c,所以相乘时两边都对1000000007取模,再保存在int64里面不会溢出
7.搜索二叉树转双向链表-二叉树左右信息归纳
牛客网链接
分析:
1.我们需要将一棵树,按双向链表进行输出,其实就是中序遍历,难点是如何将指针串起来,因为是要返回头指针,然后去遍历头指针
2.其实这里可以骗测试用例,直接按中序输出树即可。还有更粗暴的,for循环输出1到节点树,比如123456789!实测有效
3.但是,我们肯定也不可以这样做
4.在这里,就要用到递归的思想了。我们需要什么信息?从左子树和右子树拿什么信息?这里,我们可以构造一个新的数据结构,帮助我们来解决
5.一个TreeNode,里面只有两个Node,一个表示begin的Node,一个表示end的Node。begin是已经和end这两个Node内部其实是已经连起来了
6.那么我们将左子树的这个TreeNode的结果,和右子树的TreeNode的结果同时给父节点。那么父节点再把这两个TreeNode连接起来,就构成了一个新的TreeNode
7.不断的连接,最后构造出全部的节点连接
8.结束时的TreeNode的begin的Node,已经和end的Node,指针连起来了
9.核心思想就是每个节点,拿自己左子树的TreeNode和右子树的TreeNode,生成自己的TreeNode
using System;
using System.Collections.Generic;
using System.Text;
namespace test
{
class test
{
public class TreeNode
{
public Node begin;
public Node end;
public TreeNode(Node begin, Node end)
{
this.begin = begin;
this.end = end;
}
}
public class Node
{
public int val;
public Node left;
public Node right;
public Node()
{
}
public Node(int val)
{
this.val = val;
}
}
static Dictionary<int, Node> nodes = new Dictionary<int, Node>();
static void Main()
{
int len = Convert.ToInt32(Console.ReadLine());
Node nodehead = new Node(0);
//构造双链表二叉树
for (int i = 0; i < len; i++)
{
string[] getin = Console.ReadLine().Split(' ');
int val = Convert.ToInt32(getin[0]);
int left = Convert.ToInt32(getin[1]);
int right = Convert.ToInt32(getin[2]);
Node node;
if (!nodes.ContainsKey(val))
{
node = new Node(val);
nodes.Add(val, node);
}
else
{
node = nodes[val];
}
if (i == 0)
{
nodehead = node;
}
if (!nodes.ContainsKey(left)&& left != 0)
{
Node nodeleft = new Node(left);
nodes.Add(left, nodeleft);
node.left = nodeleft;
}
else if(left != 0)
{
node.left = nodes[left];
}
if (!nodes.ContainsKey(right)&&right!=0)
{
Node noderight = new Node(right);
nodes.Add(right, noderight);
node.right = noderight;
}
else if(right != 0)
{
node.right = nodes[right];
}
}
nodes.Clear();
Node beginNode = GetTreeNode(nodehead).begin;
StringBuilder stringBuilder = new StringBuilder();
while (beginNode != null)
{
stringBuilder.Append(beginNode.val + " ");
beginNode = beginNode.right;
}
Console.WriteLine(stringBuilder);
}
static TreeNode GetTreeNode(Node node)
{
if (node==null)
{
return new TreeNode(null, null);
}
TreeNode leftTreeNode = GetTreeNode(node.left);
TreeNode rightTreeNode = GetTreeNode(node.right);
//现在在某个节点Node上
//如果leftTreeNode的end节点不为空
//则让end节点Node的right指针指向当前的节点
if (leftTreeNode.end != null)
{
leftTreeNode.end.right = node;
}
//修改Node的左右指针
node.left = leftTreeNode.end;
node.right = rightTreeNode.begin;
//如果在这之前的rightTreeNode的end节点不为空
//则让begin节点Node的left指针指向当前的节点
if (rightTreeNode.begin != null)
{
rightTreeNode.begin.left = node;
}
return new TreeNode(
//如果左边begin是空的,则返回当前节点作为begin
leftTreeNode.begin != null ? leftTreeNode.begin : node,
//如果右边end是空的,则返回当前节点作为end
rightTreeNode.end != null ? rightTreeNode.end : node
);
}
}
}
8.最大搜索二叉子树节点数-二叉树左右信息归纳
分析:
1.我们先看二叉树的解题思路
2.然后我们分析这道题是X有关还是X无关
3.我们根据左树和右树的信息推出当前数的信息
4.不断递归
using System;
using System.Collections.Generic;
namespace test
{
class test
{
public class Info
{
public int MaxBSTsize;//最大搜索二叉树的大学
public int Max; //树最大值
public int Min; //树最小值
public bool IsAllBst;
public Info(int MaxBSTsize, int Max, int Min, bool IsAllBst)
{
this.MaxBSTsize = MaxBSTsize;
this.Max = Max;
this.Min = Min;
this.IsAllBst = IsAllBst;
}
}
public class Node
{
public int val;
public Node left;
public Node right;
public Node(int val, Node left, Node right)
{
this.val = val;
this.left = left;
this.right = right;
}
}
static Dictionary<int, Node> dics = new Dictionary<int, Node>();
static void Main()
{
int len = Convert.ToInt32(Console.ReadLine().Split(' ')[0]);
Node head = new Node(0, null, null);
for (int i = 0; i < len; i++)
{
string[] putin = Console.ReadLine().Split(' ');
int val = Convert.ToInt32(putin[0]);
int left = Convert.ToInt32(putin[1]);
int right = Convert.ToInt32(putin[2]);
Node node;
if (dics.ContainsKey(val))
{
node = dics[val];
}
else
{
node = new Node(val, null, null);
dics.Add(val, node);
}
if (i == 0)
{
head = node;
}
if (left != 0)
{
if (dics.ContainsKey(left))
{
node.left = dics[left];
}
else
{
Node nodeleft = new Node(left, null, null);
node.left = nodeleft;
dics.Add(left, nodeleft);
}
}
if (right != 0)
{
if (dics.ContainsKey(right))
{
node.right = dics[right];
}
else
{
Node noderight = new Node(right, null, null);
node.right = noderight;
dics.Add(right, noderight);
}
}
}
//Console.WriteLine(head.val+" "+head.left.val);
int Mmax = GetInfo(head).MaxBSTsize;
Console.WriteLine(Mmax);
}
static Info GetInfo(Node x)
{
if (x == null)
{
return null;
}
Info leftInfo = GetInfo(x.left);
Info rightInfo = GetInfo(x.right);
//拿到左边和右边的info之后,生成当前节点的info
int XmaxBSTsize = 0;
bool XisBst = false;
//左边和右边的最大搜索二叉树的值
int p1 = -1;
int p2 = -1;
if (leftInfo != null)
{
p1 = leftInfo.MaxBSTsize;
}
if (rightInfo != null)
{
p2 = rightInfo.MaxBSTsize;
}
//新的最大搜索二叉树的值
int p3 = -1;
//
if (
//左树是不是最大搜索二叉树
(leftInfo == null ? true : leftInfo.IsAllBst)
&&
//右树是不是最大搜索二叉树
(rightInfo == null ? true : rightInfo.IsAllBst)
&&
//左子树的最大值是不是小于X
(leftInfo == null ? true : leftInfo.Max < x.val)
&&
//右子树的最小值是不是大于X
(rightInfo == null ? true : rightInfo.Min > x.val)
)
{
//都满足的话,代表左右都是满足条件的树,现在将两个融合
p3 = (leftInfo == null ? 0 : leftInfo.MaxBSTsize) +
(rightInfo == null ? 0 : rightInfo.MaxBSTsize) + 1;
//当前节点是一个满足条件的树
XisBst = true;
}
XmaxBSTsize = Math.Max(p1, Math.Max(p2, p3));
//新的数的最大值和最小值
int MaxVal = x.val;
int MinVal = x.val;
if (leftInfo != null)
{
MaxVal = Math.Max(leftInfo.Max, MaxVal);
MinVal = Math.Min(leftInfo.Min, MinVal);
}
if (rightInfo != null)
{
MaxVal = Math.Max(rightInfo.Max, MaxVal);
MinVal = Math.Min(rightInfo.Min, MinVal);
}
return new Info(XmaxBSTsize, MaxVal, MinVal, XisBst);
}
}
}
9.完全二叉树节点数-递归
牛客网链接
分析:
什么是完全二叉树?
完全二叉树是满二叉树的子集,意思就是,完全二叉树只可能小于等于满二叔的节点个数
同时,完全二叉树必须得是最后一层从左往右填的。
using System;
using System.Collections.Generic;
/*
public class TreeNode
{
public int val;
public TreeNode left;
public TreeNode right;
public TreeNode (int x)
{
val = x;
}
}
*/
class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param head TreeNode类
* @return int整型
*/
public int nodeNum (TreeNode head) {
//Console.WriteLine(124134134);
if(head==null)return 0;
return Bs(head,1,MostLeftLevel(head, 1));
//return 1;
}
public static int Bs(TreeNode node,int level,int h){
if(level==h)return 1;
if((MostLeftLevel(node.right, level+1)==h)){
//这里右树的最左深度一直,表示左树全为满
return (1<<h-level)+Bs(node.right,level+1,h);
}else{
//左树并不满,右树为满
return (1<<h-level-1)+Bs(node.left, level+1,h);
}
}
public static int MostLeftLevel(TreeNode node,int level){
while(node!=null){
level++;
node=node.left;
}
return level-1;
}
}