第八屆藍橋杯單片機省賽

什么?你把最近幾屆省賽真題做完已經無題可做了,那不妨來看看老古董第八屆省賽的題目吧!

附件:第八屆藍橋杯單片機省賽
在這里插入圖片描述

一、數碼管

1.頁面流轉

在這里插入圖片描述

在這里插入圖片描述
在這里插入圖片描述
以上的頁面流轉功能可以用下圖總結:

長按S4
松開S4
按下S7
按下S6
時間顯示頁面
溫度顯示頁面
時鐘設置
鬧鐘設置

這邊給出一種實現方法:

  • 定義unsigned char型變量SetMode來控制三個主要頁面(SetMode=0:時間顯示頁面、SetMode=1:時鐘設置頁面、SetMode=2:鬧鐘設置頁面)
  • 然后定義bit 型變量SegMode在時間顯示頁面(SetMode=0)單獨控制溫度顯示頁面(SegMode=0:時間顯示頁面、SegMode=1:溫度顯示頁面)

2.變量初定義

關于時鐘設置和鬧鐘設置是在設置期間數據就生效還是退出到時間顯示頁面才生效這個問題,題目并沒有明確指出,本篇文章默認時鐘設置和鬧鐘設置在退出到時間顯示頁面才生效。

typedef unsigned char u8;
typedef unsigned int u16;
/* 頁面參數 */	
idata u8 SetMode; //0-時間頁面 1-時間設置 2-鬧鐘設置
idata bit SegMode;//0-時間顯示 1-溫度顯示
/* 時鐘、鬧鐘參數 */
idata u8 Index;//0-無 1-時 2-分 3-秒
pdata u8 Rtc[3] = {0x23,0x59,0x50};//時鐘顯示數據
pdata u8 RtcSet[3];//時鐘設置時修改的數據
pdata u8 Alarm[3] = {0x00,0x00,0x00};//鬧鐘存放時間
pdata u8 AlarmSet[3];//鬧鐘設置時修改的數據
/* 溫度 */
idata u8 tem;	
/* 老朋友  */
idata u8 SegPos;
pdata u8 SegBuf[8] = {10,10,10,10,10,10,10,10};
pdata u8 SegPoint[8] = {0,0,0,0,0,0,0,0};
pdata u8 ucLed[8] = {0,0,0,0,0,0,0,0};

3.時間顯示頁面

在這里插入圖片描述

  • ds1302.h
#ifndef __ds1302_H__
#define __ds1302_H__void SetRtc(unsigned char *Rtc);
void GetRtc(unsigned char *Rtc);#endif
  • ds1302.c
#include <STC15F2K60S2.H>
#include <intrins.h>sbit SCK = P1^7;
sbit SDA = P2^3;
sbit RST = P1^3;void Write_Ds1302(unsigned  char temp) 
{unsigned char i;for (i=0;i<8;i++)     	{ SCK = 0;SDA = temp&0x01;temp>>=1; SCK=1;}
}   //
void Write_Ds1302_Byte( unsigned char address,unsigned char dat )     
{RST=0;	_nop_();SCK=0;	_nop_();RST=1; 	_nop_();  Write_Ds1302(address);	Write_Ds1302(dat);		RST=0; 
}//
unsigned char Read_Ds1302_Byte ( unsigned char address )
{unsigned char i,temp=0x00;RST=0;	_nop_();SCK=0;	_nop_();RST=1;	_nop_();Write_Ds1302(address);for (i=0;i<8;i++) 	{		SCK=0;temp>>=1;	if(SDA)temp|=0x80;	SCK=1;} RST=0;	_nop_();SCK=0;	_nop_();SCK=1;	_nop_();SDA=0;	_nop_();SDA=1;	_nop_();return (temp);			
}code unsigned char DS1302[4] = {0x84,0x82,0x80,0x8E};void SetRtc(unsigned char *Rtc)
{unsigned char i;Write_Ds1302_Byte(DS1302[3],0x00);for(i = 0; i < 3; i++)Write_Ds1302_Byte(DS1302[i],Rtc[i]);Write_Ds1302_Byte(DS1302[3],0x80);
}
void GetRtc(unsigned char *Rtc)
{unsigned char i;for(i = 0; i < 3; i++)Rtc[i] = Read_Ds1302_Byte(DS1302[i]+1);
}
  • main.c
void SegProc()
{unsigned char i;GetRtc(Rtc);//讀取當前時間switch(SetMode){case 0:if(!SegMode){SegBuf[2] = SegBuf[5] = 11;for(i = 0; i < 3; i++){				SegBuf[3*i] = Rtc[i] / 16;SegBuf[3*i+1] = Rtc[i] % 16;}}break;}
}void main()
{SystemInit();Timer0_Init();SetRtc(Rtc);//上電時設置時間while(1){//...}
}

4.溫度顯示頁面

在這里插入圖片描述

  • ds18b20.h
#ifndef __ds18b20_H__
#define __ds18b20_H__float TemRead();#endif
  • ds18b20.c
#include <STC15F2K60S2.H>sbit DQ = P1^4;void Delay_OneWire(unsigned int t)  
{unsigned char i;while(t--){for(i=0;i<12;i++);}
}//
void Write_DS18B20(unsigned char dat)
{unsigned char i;for(i=0;i<8;i++){DQ = 0;DQ = dat&0x01;Delay_OneWire(5);DQ = 1;dat >>= 1;}Delay_OneWire(5);
}//
unsigned char Read_DS18B20(void)
{unsigned char i;unsigned char dat;for(i=0;i<8;i++){DQ = 0;dat >>= 1;DQ = 1;if(DQ){dat |= 0x80;}	    Delay_OneWire(5);}return dat;
}//
bit init_ds18b20(void)
{bit initflag = 0;DQ = 1;Delay_OneWire(12);DQ = 0;Delay_OneWire(80);DQ = 1;Delay_OneWire(10); initflag = DQ;     Delay_OneWire(5);return initflag;
}float TemRead()
{unsigned char tem_low, tem_hig;init_ds18b20();Write_DS18B20(0xcc);Write_DS18B20(0x44);init_ds18b20();Write_DS18B20(0xcc);Write_DS18B20(0xbe);tem_low = Read_DS18B20();tem_hig = Read_DS18B20();return ((tem_hig << 8) | tem_low) / 16.0;
}
  • main.c
void SegProc()
{tem = TemRead();switch(SetMode){case 0:if(!SegMode){//...}else//溫度顯示頁面{SegBuf[0] = 10;SegBuf[1] = 10;SegBuf[2] = 10;SegBuf[3] = 10;SegBuf[4] = 10;SegBuf[5] = tem / 10;SegBuf[6] = tem % 10;SegBuf[7] = 12;//C}break;}
}

5.時鐘設置頁面

在這里插入圖片描述
只顯示時鐘設置的數據:

for(i = 0; i < 3; i++)
{				SegBuf[3*i] = RtcSet[i] / 16;SegBuf[3*i+1] = RtcSet[i] % 16;
}

根據按鍵S7去選擇待調整的時、分、秒,當前選擇的顯示單元以 1 秒為間隔亮滅。
可以通過Index來選擇時、分、秒,選中后,定義計時變量idata u16 Time_1s;來計時1s讓idata bit SegFlash;取反達到數碼管閃爍的效果(和指示燈閃爍一樣)。

idata u16 Time_1s;
idata bit SegFlash;void SegProc()
{//...switch(SetMode){case 0://...break;case 1://時鐘設置頁面SegBuf[2] = SegBuf[5] = 11;for(i = 0; i < 3; i++){				SegBuf[3*i] = (Index == i+1 && SegFlash) ? 10 : RtcSet[i] / 16;SegBuf[3*i+1] = (Index == i+1 && SegFlash) ? 10 : RtcSet[i] % 16;}break;}
}void Timer0_Isr(void) interrupt 1
{if(++SegPos == 8) SegPos = 0;SegDisp(SegPos,SegBuf[SegPos],SegPoint[SegPos]);if(++Time_1s == 1000){Time_1s = 0;SegFlash = !SegFlash;}
}

6.鬧鐘設置頁面

在這里插入圖片描述
鬧鐘設置頁面不用考慮數碼管閃爍

void SegProc()
{u8 i;switch(SetMode){case 2:SegBuf[2] = SegBuf[5] = 11;for(i = 0; i < 3; i++){				SegBuf[3*i] = AlarmSet[i] / 16;SegBuf[3*i+1] = AlarmSet[i] % 16;}break;}
}

7.數碼管完整代碼

typedef unsigned char u8;
typedef unsigned int u16;
/* 頁面參數 */	
idata u8 SetMode; //0-時間頁面 1-時間設置 2-鬧鐘設置
idata bit SegMode;//0-時間顯示 1-溫度顯示
/* 時鐘、鬧鐘參數 */
idata u8 Index;//0-無 1-時 2-分 3-秒
pdata u8 Rtc[3] = {0x23,0x59,0x50};//時鐘顯示數據
pdata u8 RtcSet[3];//時鐘設置時修改的數據
pdata u8 Alarm[3] = {0x00,0x00,0x00};//鬧鐘存放時間
pdata u8 AlarmSet[3];//鬧鐘設置時修改的數據
/* 溫度 */
idata u8 tem;	void SegProc()
{unsigned char i;tem = TemRead();GetRtc(Rtc);switch(SetMode){case 0:if(!SegMode){SegBuf[2] = SegBuf[5] = 11;for(i = 0; i < 3; i++){				SegBuf[3*i] = Rtc[i] / 16;SegBuf[3*i+1] = Rtc[i] % 16;}}else{SegBuf[0] = 10;SegBuf[1] = 10;SegBuf[2] = 10;SegBuf[3] = 10;SegBuf[4] = 10;SegBuf[5] = tem / 10;SegBuf[6] = tem % 10;SegBuf[7] = 12;}break;case 1:SegBuf[2] = SegBuf[5] = 11;for(i = 0; i < 3; i++){				SegBuf[3*i] = (Index == i+1 && SegFlash) ? 10 : RtcSet[i] / 16;SegBuf[3*i+1] = (Index == i+1 && SegFlash) ? 10 : RtcSet[i] % 16;}break;case 2:SegBuf[2] 	= SegBuf[5] = 11;for(i = 0; i < 3; i++){				SegBuf[3*i] = AlarmSet[i] / 16;SegBuf[3*i+1] = AlarmSet[i] % 16;}break;}
}void Timer0_Init(void)		//1毫秒@12.000MHz
{AUXR &= 0x7F;			//定時器時鐘12T模式TMOD &= 0xF0;			//設置定時器模式TL0 = 0x18;			  //設置定時初始值TH0 = 0xFC;				//設置定時初始值TF0 = 0;				  //清除TF0標志TR0 = 1;				  //定時器0開始計時ET0 = 1;				  //使能定時器0中斷EA = 1;
}void Timer0_Isr(void) interrupt 1
{if(++SegPos == 8) SegPos = 0;SegDisp(SegPos,SegBuf[SegPos],SegPoint[SegPos]);if(++Time_1s == 1000){Time_1s = 0;SegFlash = !SegFlash;}
}

二、按鍵

1.S4、S5

在這里插入圖片描述
在設置時鐘和鬧鐘的時候,由于要考慮參數的邊界性,所以在加和減參數的時候要考慮最大值和最小值,防止數據越界,小時的范圍是00 ~ 23,分鐘的范圍是00 ~ 59,秒鐘的范圍是00 ~ 59,這邊需要注意的是,unsigned char的數據范圍是0~255,假設參數減到0,再按下S4后,下一次的值是255。

void KeyProc()
{KeyVal = KeyDisp();KeyDown = KeyVal & ~KeyOld;KeyUp = ~KeyVal & KeyOld;KeyOld = KeyVal;switch(KeyDown){case 5:if(SetMode == 1)//時間設置{RtcSet[Index-1]++;if(RtcSet[0] == 24)RtcSet[0] = 23;if(RtcSet[1] == 60)RtcSet[1] = 59;if(RtcSet[2] == 60)RtcSet[2] = 59;}else if(SetMode == 2)//鬧鐘設置{AlarmSet[Index-1]++;if(AlarmSet[0] == 24)AlarmSet[0] = 23;if(AlarmSet[1] == 60)AlarmSet[1] = 59;if(AlarmSet[2] == 60)AlarmSet[2] = 59;}break;case 4:if(SetMode == 1){RtcSet[Index-1]--;if(RtcSet[Index-1] == 255)RtcSet[Index-1] = 0;}else if(SetMode == 2){AlarmSet[Index-1]--;if(AlarmSet[Index-1] == 255)AlarmSet[Index-1] = 0;}break;}
}

1.S6、S7

對于S6、S7。只需要在進入小時的設置時將原先的時鐘/鬧鐘數據傳給AlarmSet/AlarmSet,完成設置返回時間顯示頁面時再將AlarmSet/AlarmSet的值傳回時鐘/鬧鐘。

switch(KeyDown){case 6:Index++;if(Index == 1){SetMode = 2;memcpy(AlarmSet,Alarm,3);}if(Index == 4){SetMode = 0;memcpy(Alarm,AlarmSet,3);Index = 0;}break;case 7:Index++;if(Index == 1){SetMode = 1;memcpy(RtcSet,Rtc,3);}if(Index == 4){SetMode = 0;memcpy(Rtc,RtcSet,3);Index = 0;SetRtc(Rtc);}break;

3.完整代碼

void KeyProc()
{KeyVal = KeyDisp();KeyDown = KeyVal & ~KeyOld;KeyUp = ~KeyVal & KeyOld;KeyOld = KeyVal;//長按S4進入溫度顯示頁面,松手返回時間顯示頁面if(!SetMode){if(KeyOld == 4)SegMode = 1;if(KeyUp == 4)SegMode = 0;}switch(KeyDown){case 6:Index++;if(Index == 1){SetMode = 2;memcpy(AlarmSet,Alarm,3);}if(Index == 4){SetMode = 0;memcpy(Alarm,AlarmSet,3);Index = 0;}break;case 7:Index++;if(Index == 1){SetMode = 1;memcpy(RtcSet,Rtc,3);}if(Index == 4){SetMode = 0;memcpy(Rtc,RtcSet,3);Index = 0;SetRtc(Rtc);}break;case 5:if(SetMode == 1){RtcSet[Index-1]++;if(RtcSet[0] == 24)RtcSet[0] = 23;if(RtcSet[1] == 60)RtcSet[1] = 59;if(RtcSet[2] == 60)RtcSet[2] = 59;}else if(SetMode == 2){AlarmSet[Index-1]++;if(AlarmSet[0] == 24)AlarmSet[0] = 23;if(AlarmSet[1] == 60)AlarmSet[1] = 59;if(AlarmSet[2] == 60)AlarmSet[2] = 59;}break;case 4:if(SetMode == 1){RtcSet[Index-1]--;if(RtcSet[Index-1] == 255)RtcSet[Index-1] = 0;}else if(SetMode == 2){AlarmSet[Index-1]--;if(AlarmSet[Index-1] == 255)AlarmSet[Index-1] = 0;}break;}
}

三、指示燈

在這里插入圖片描述
先實現鬧鐘響的時候L1以200ms為間隔閃爍,持續5s。

idata u8 Time_200ms;//閃爍間隔
idata u16 Time_5s;//定時5s
idata bit AlarmFlag;//鬧鐘響
idata bit LedFlash;//指示燈閃爍void LedProc()
{//鬧鐘響了if(Alarm[0] == Rtc[0] && Alarm[1] == Rtc[1] && Alarm[2] == Rtc[2])AlarmFlag = 1;ucLed[0] = LedFlash;LedDisp(ucLed);
}void Timer0_Isr(void) interrupt 1
{systick++;if(++SegPos == 8) SegPos = 0;SegDisp(SegPos,SegBuf[SegPos],SegPoint[SegPos]);if(AlarmFlag)//鬧鐘響{Time_5s++;//開始計時5sif(Time_5s == 5000)//計時到5s關閉鬧鐘{Time_5s = 0;AlarmFlag = 0;//關閉鬧鐘}else//還未計時5秒,L1閃爍{if(++Time_200ms == 200){Time_200ms = 0;LedFlash = !LedFlash;}}}elseLedFlash = 0;
}

接下來寫在任意按鍵按下關閉鬧鐘

void KeyProc()
{//...if(AlarmFlag){if(KeyDown)//按鍵值不為0AlarmFlag = 0;//關閉鬧鐘//防止這次的按鍵干擾到其他功能按鍵,關閉鬧鐘直接退出不執行下面語句return;}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/72997.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/72997.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/72997.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

win10電腦鼠標速度突然變的很慢?

電腦鼠標突然變很慢&#xff0c;殺毒檢測后沒問題&#xff0c;鼠標設置也沒變&#xff0c;最后發現可能是誤觸鼠標的“DPI”調節鍵。 DPI調節鍵在鼠標滾輪下方&#xff0c;再次點擊即可恢復正常鼠標速度。 如果有和-的按鍵&#xff0c;速度變快&#xff0c;-速度變慢。 圖源&…

1-002:MySQL InnoDB引擎中的聚簇索引和非聚簇索引有什么區別?

在 MySQL InnoDB 存儲引擎 中&#xff0c;索引主要分為 聚簇索引&#xff08;Clustered Index&#xff09; 和 非聚簇索引&#xff08;Secondary Index&#xff09;。它們的主要區別如下&#xff1a; 1. 聚簇索引&#xff08;Clustered Index&#xff09; 定義 聚簇索引是表數…

【解決哈希沖突】

哈希沖突 如果兩個不同的 key 通過哈希函數得到了相同的索引&#xff0c;這種情況就叫做「哈希沖突」。 哈希沖突不可能避免&#xff0c;只能在算法層面妥善處理出現哈希沖突的情況。 哈希沖突是一定會出現的&#xff0c;因為這個 hash 函數相當于是把一個無窮大的空間映射到…

文件操作詳解(萬字長文)

C語言文件操作 一、為什么使用文件&#xff1f;二、文件分類三、文件的打開和關閉四、文件的順序讀寫4.1fputc4.2fgetc4.3fputs4.4fgets4.5 fprintf4.6 fscanf4.7 fwrite4.8 fread 五、文件的隨機讀寫5.1 fseek5.2 ftell和rewind六、文件讀取結束的判定七、文件緩沖區 一、為什…

基于 JDBC 的后端與 MySQL 數據庫交互 javaweb

一、了解JDBC 二、添加MySQL的JDBC驅動包 三、使用JDBC連接數據庫應用&#x1f517; 3.1創建一個包 3.2 查找實例 3.3 修改添加刪除實例 四、封裝 &#x1f4e6; DBConnection.java MysqlUtil.java 測試使用一下 測試1 測試2 在后端開發中&#xff0c;與數據庫進行交…

貪心算法--

1.檸檬水找零 link:860. 檸檬水找零 - 力扣&#xff08;LeetCode&#xff09; code class Solution { public:bool lemonadeChange(vector<int>& bills) {// 貪心算法&#xff0c; 優先花出大面額bill&#xff0c; 盡可能保護小面額billint five 0, ten 0;// 不…

基于YOLO11深度學習的電瓶車進電梯檢測與語音提示系統【python源碼+Pyqt5界面+數據集+訓練代碼】

《------往期經典推薦------》 一、AI應用軟件開發實戰專欄【鏈接】 項目名稱項目名稱1.【人臉識別與管理系統開發】2.【車牌識別與自動收費管理系統開發】3.【手勢識別系統開發】4.【人臉面部活體檢測系統開發】5.【圖片風格快速遷移軟件開發】6.【人臉表表情識別系統】7.【…

github生成badges的方法

在Github頁面上生成類似下面這樣的badge的方法 你可以通過以下步驟在GitHub個人主頁的README中創建類似的技術棧徽章&#xff1a; 一、使用 Shields.io 生成徽章 Shields.io 是一個開源徽章生成工具&#xff0c;支持自定義文本、顏色、圖標等參數。 1. 基礎模板 https://…

vue3 二次封裝uni-ui中的組件,并且組件中有 v-model 的解決方法

在使用uniappvue3開發中&#xff0c; 使用了uni-ui的組件&#xff0c;但是我們也需要自定義組件&#xff0c;比如我要自定一個picker 的組件&#xff0c; 是在 uni-data-picker 組件的基礎上進行封裝的 父組件中的代碼 <classesselect :selectclass"selectclass"…

Spring Boot啟動流程及源碼實現深度解析

Spring Boot啟動流程及源碼實現深度解析 一、啟動流程概述 Spring Boot的啟動流程圍繞SpringApplication類展開&#xff0c;核心流程可分為以下幾個階段&#xff1a; 初始化階段&#xff1a;推斷應用類型&#xff0c;加載ApplicationContextInitializer和ApplicationListene…

爬蟲案例七Python協程爬取視頻

提示&#xff1a;文章寫完后&#xff0c;目錄可以自動生成&#xff0c;如何生成可參考右邊的幫助文檔 文章目錄 前言一、Python協程爬取視頻 前言 提示&#xff1a;這里可以添加本文要記錄的大概內容&#xff1a; 爬蟲案例七協程爬取視頻 提示&#xff1a;以下是本篇文章正文…

uni-app開發的App和H5嵌套封裝的App,以及原生App有什么區別

uni-app 開發的 App 和 H5 嵌套封裝的 App 是兩種不同的開發模式&#xff0c;雖然它們都可以實現跨平臺開發&#xff0c;但在技術實現、性能、功能支持等方面有顯著區別。以下是詳細對比&#xff1a; 1. uni-app 開發的 App uni-app 是一個基于 Vue.js 的跨平臺開發框架&#…

Python 爬蟲實戰案例 - 獲取拉勾網招聘職位信息

引言 拉勾網&#xff0c;作為互聯網招聘領域的佼佼者&#xff0c;匯聚了海量且多樣的職位招聘信息。這些信息涵蓋了從新興科技領域到傳統行業轉型所需的各類崗位&#xff0c;無論是初出茅廬的應屆生&#xff0c;還是經驗豐富的職場老手&#xff0c;都能在其中探尋到機遇。 對…

LM Studio 替換源的方式解決huggingface.co無法訪問的問題

安裝軟件完成之后&#xff0c;不要打開&#xff0c;打開了就直接關閉 在安裝目錄下&#xff0c;比如我安裝在E:\Program Files\LM Studio 下面三個文件中的huggingface.co全部替換為hf-mirror.com然后再打開即可。 E:\Program Files\LM Studio\resources\app\.webpack\rende…

【模擬CMOS集成電路設計】帶隙基準(Bandgap)設計與仿真(基于運放的電流模BGR)

【模擬CMOS集成電路設計】帶隙基準&#xff08;Bandgap&#xff09;設計與仿真 前言工程文件&部分參數計算過程&#xff0c;私聊~ 一、 設計指標指標分析&#xff1a; 二、 電路分析三、 仿真3.1仿真電路圖3.2仿真結果(1)運放增益(2)基準溫度系數仿真(3)瞬態啟動仿真(4)靜態…

微服務拆分-遠程調用

我們在查詢購物車列表的時候&#xff0c;它有一個需求&#xff0c;就是不僅僅要查出購物車當中的這些商品信息&#xff0c;同時還要去查到購物車當中這些商品的最新的價格和狀態信息&#xff0c;跟購物車當中的快照進行一個對比&#xff0c;從而去提醒用戶。 現在我們已經做了服…

機動車授權簽字人考試的報名條件是什么?

機動車授權簽字人考試的報名條件通常如下&#xff1a; 學歷職稱與工作經驗要求 中級職稱及以上&#xff1a;應具備中級及以上專業技術職稱&#xff0c;且從事相關檢驗檢測工作三年及以上。如果承檢車型有專項作業車、大型客車、校車和危險貨物運輸車等&#xff0c;若不是相關專…

智慧工廠監測信息系統:構筑安全的數字化未來

在現代工業的浪潮中&#xff0c;智慧工廠已成為推動生產效率和產品質量提升的關鍵力量。為了確保這一先進生產模式的穩健運行&#xff0c;智慧工廠監測信息系統應運而生&#xff0c;并通過一系列安全措施&#xff0c;為企業的數字化轉型保駕護航。 安全注冊&#xff0c;筑牢第…

P2P中NAT穿越方案(UDP/TCP)(轉)

轉自&#xff1a;P2P中NAT穿越方案&#xff08;UDP/TCP&#xff09;_udp反向鏈接-CSDN博客 同&#xff1a;P2P中NAT穿越方案&#xff08;UDP/TCP&#xff09; - 知乎 (zhihu.com) 本文介紹了傳統基于udp的打洞方式&#xff0c;更進一步闡述了tcp打洞的原理&#xff0c;是對于…

算法 之 樹形dp 樹的中心、重心

文章目錄 重心實踐題目小紅的陡峭值 在樹的算法中&#xff0c;求解樹的中心和重心是一類十分重要的算法 求解樹的重心 樹的重心的定義&#xff1a;重心是樹中的一個節點&#xff0c;如果將這個點刪除后&#xff0c;剩余各個連通塊中點數的最大值最小&#xff0c;那么這個節點…