重温DFS
return 是遞歸中聯(lián)系上下層的重要點(diǎn),必須要深刻了解何時(shí)用何時(shí)不用的區(qū)別
自寫組合算法
dfs時(shí),選或不選第k個(gè)數(shù),就實(shí)現(xiàn)了各種組合。
打印二進(jìn)制數(shù)
vis=[0]*10 def dfs(k,s):if k==3:print(s)else:vis[k]=0#選第k個(gè)數(shù)dfs(k+1,s+'0')vis[k]=1#不選第k個(gè)數(shù)dfs(k+1,s+'1') dfs(0,'')DFS連通塊
全球變暖
import sys sys.setrecursionlimit(60000) n=int(input()) mg=[] for i in range(n):mg.append(list(input()))vis=[[0]*n for i in range(n)]def dfs(x,y):global flagf=1vis[x][y]=1#注意不用回溯for dx,dy in [(1,0),(0,1),(0,-1),(-1,0)]:nx=dx+xny=dy+yif 0<=nx<n and 0<ny<=n:if mg[nx][ny]=='#':if vis[nx][ny]!=1:dfs(nx,ny)else:f=0 #說明x,y周圍不都有岸,x,y會沉下去if f:#說明x,y周圍都有岸,x,y不會沉下去flag=1 cnt=0 for i in range(n):for j in range(n):if mg[i][j]=='#' and vis[i][j]!=1:#print(vis)flag=0dfs(i,j)if flag==0:#記錄會沉下去的島cnt+=1 print(cnt)樹上DFS
樹的重心
樹的重心u:
????????以樹上任意一個(gè)結(jié)點(diǎn)為根計(jì)算它的子樹的結(jié)點(diǎn)數(shù),如果結(jié)點(diǎn)u的最大的子樹的結(jié)點(diǎn)數(shù)最少,那么u就是樹的重心。即刪除點(diǎn)u后得到兩棵或更多棵互不連通的子樹,其中最大子樹的結(jié)點(diǎn)數(shù)最小。u是樹上最平衡的點(diǎn)。
那么如何計(jì)算結(jié)點(diǎn)的數(shù)量呢,
教父
| 時(shí)間限制:?2000MS | 內(nèi)存限制:?65536K |
描述
去年芝加哥充滿了黑幫斗毆和奇怪的謀殺案。警察局長真的厭倦了所有這些罪行,并決定逮捕黑手黨領(lǐng)導(dǎo)人。
不幸的是,芝加哥黑手黨的結(jié)構(gòu)相當(dāng)復(fù)雜。已知有n個(gè)人與黑手黨有關(guān)。警方追蹤他們的活動有一段時(shí)間了,知道他們中的一些人正在互相交流。根據(jù)收集到的數(shù)據(jù),警察局長建議可以將黑手黨等級制度表示為一棵樹。黑手黨的頭目教父是樹的根,如果用樹中的一個(gè)節(jié)點(diǎn)表示某個(gè)人,則其直屬下屬就是該節(jié)點(diǎn)的子節(jié)點(diǎn)。為了陰謀,歹徒只與他們的直接下屬和他們的直接主人聯(lián)系。
不幸的是,雖然警察知道歹徒的通訊方式,但他們不知道任何一對通訊人員中誰是高手。因此他們只有一棵無向的通信樹,并且不知道教父是誰。
基于教父希望對黑手黨有最大可能的控制的想法,警察局長提出了一個(gè)建議,教父是這樣一個(gè)人,從通信樹中刪除它后,最大的剩余連接組件的大小盡可能小盡可能。幫助警察找到所有可能的教父,他們會逮捕他們。
輸入
輸入文件的第一行包含n?— 被懷疑屬于黑手黨的人數(shù)(2 ≤?n?≤ 50 000)。讓它們從 1 到n編號。
接下來的n?-1 行每行包含兩個(gè)整數(shù)。一對a?i?,?b?i表示歹徒a?i與歹徒b?i進(jìn)行了通信。保證黑幫的通訊形成一棵樹。
輸出
打印所有被懷疑是教父的人的人數(shù)。數(shù)字必須按遞增順序打印,并以空格分隔。
樣本輸入
6 1 2 2 3 2 5 3 4 3 6示例輸出
2 3 import sys sys.setrecursionlimit(300000) def dfs(u,fa):global maxnglobal numtmp=0d[u]=1for er in edges[u]:#遍歷u的子節(jié)點(diǎn)if er==fa:#不遞歸父親continuedfs(er,u)#遞歸子節(jié)點(diǎn),計(jì)算er這個(gè)子樹的節(jié)點(diǎn)數(shù)量d[u]+=d[er]#計(jì)算以u為根的節(jié)點(diǎn)數(shù)量tmp=max(tmp,d[er])#記錄u的最大子樹的節(jié)點(diǎn)數(shù)量tmp=max(tmp,n-d[u])#與u父親相連的另一半節(jié)點(diǎn)數(shù)量#以上計(jì)算出了u的最大連通塊#下面計(jì)算疑似教父,如果每一個(gè)節(jié)點(diǎn)的最大連通塊比其他節(jié)點(diǎn)的都小,它疑似教父if tmp<maxn:#一個(gè)疑似教父maxn=tmp#更新‘最小的’最大連通塊num=1ans[num]=u#把教父記錄在第一個(gè)位置elif tmp==maxn:num+=1ans[num]=u#如果疑似教父有多個(gè),記錄在后面 maxn=int(1e9) n=int(input()) edges=[[] for i in range(n+1)] d=[0]*(n+1) ans=[0]*(n+1) num=0 for i in range(n-1):a,b=map(int,input().split())edges[a].append(b)edges[b].append(a) dfs(1,0) s=sorted(ans[1:num+1])#對教父排序 for i in range(num):print(s[i],end=' ')#按順序打印所有教父求樹的最長直徑(非負(fù)權(quán)邊)
進(jìn)行兩次DFS:
從點(diǎn)a開始找最長的邊,其終點(diǎn)為b,再從點(diǎn)b開始找到最長的邊,其終點(diǎn)為c,則從b到c就是最長的邊,
貪心證明:將樹當(dāng)作繩子,將繩子拉到最長,其上面的節(jié)點(diǎn)會下垂,上面任意的節(jié)點(diǎn)所能到達(dá)的最長邊的終點(diǎn)必定是兩端點(diǎn)其中的一個(gè),負(fù)責(zé)繩子一開始就不是拉著最長的狀態(tài)。
import sys sys.setrecursionlimit(300000) def dfs(u,fa,d):#用dfs計(jì)算從u到每個(gè)子節(jié)點(diǎn)的距離dist[u]=dfor er,w in edges[u]:if er!=fa:#關(guān)鍵,不回頭搜素父節(jié)點(diǎn)dfs(er,u,d+w) n=int(input()) edges=[[] for i in range(n+1)] dist=[0]*(n+1)#記錄距離 for i in range(n-1):a,b,w=map(int,input().split())edges[a].append((b,w))edges[b].append((a,w)) dfs(1,-1,0) s=1 for i in range(1,n+1):#找到最遠(yuǎn)的節(jié)點(diǎn)s,s是直徑的一個(gè)端點(diǎn)if dist[i]>dist[s]:s=i dfs(s,-1,0)#從s出發(fā),計(jì)算以s為起點(diǎn),到樹上每個(gè)節(jié)點(diǎn)的距離 t=1 for i in range(1,n+1):#找到直徑的另一個(gè)端點(diǎn)tif dist[i]>dist[t]:t=i print(dist[t])#打印樹的直徑長度DFS拓?fù)渑判?/strong>
圖+有依賴關(guān)系+有向+無環(huán)=拓?fù)渑判?/p>
歐拉路與DFS
歐拉路:從圖中某個(gè)點(diǎn)出發(fā),遍歷整個(gè)圖,圖中每條邊通過且只通過一次。
????????圖中所有的點(diǎn)連接的邊是偶數(shù)倍,或者有且僅有起點(diǎn)和終點(diǎn)是奇數(shù)邊,其他點(diǎn)均為偶數(shù)邊。
歐拉回路:起點(diǎn)和終點(diǎn)相同的歐拉路。
????????圖中所有的點(diǎn)連接的邊是偶數(shù)倍
歐拉路問題:是否存在歐拉路、打印出歐拉路。
DFS剪枝
剪格子
m,n=map(int,input().split()) # 輸入m和n mg=[] for i in range(n):mg.append(list(map(int,input().split()))) numsum=0 # 初始化numsum for i in range(n):numsum+=sum(mg[i]) # 計(jì)算總和 vis=[[0]*m for i in range(n)] # 初始化vis ans=100000 # 初始化ansdef dfs(x,y,c,s):# 定義dfs函數(shù)global ans,numsum# 定義全局變量ans# 將點(diǎn)設(shè)置為已訪問if 2*s>numsum:# 如果總和大于numsum,則返回returnif 2*s==numsum:# 如果總和等于numsum,則比較ans和cif ans>c and vis[0][0]==1:# 如果ans大于c,則更新ansans=c# 將c賦值給ansreturnvis[x][y]=1for dx,dy in [(1,0),(-1,0),(0,1),(0,-1)]:# 遍歷四個(gè)方向nx=dx+xny=dy+y# 計(jì)算點(diǎn)的坐標(biāo)if 0<=nx<n and 0<=ny<m and vis[nx][ny]!=1:# 如果點(diǎn)未訪問,且不是邊界點(diǎn),則調(diào)用dfs函數(shù)dfs(nx,ny,c+1,s+mg[x][y])vis[x][y]=0# 將點(diǎn)設(shè)置為未訪問 dfs(0,0,0,0) # 調(diào)用dfs函數(shù) print(ans)路徑之謎
超時(shí)
n=int(input()) north=list(map(int,input().split())) west=list(map(int,input().split())) mg=[[0]*n for i in range(n)] vis=[[0]*n for i in range(n)] a=0 for i in range(n):for j in range(n):mg[i][j]=aa+=1 # for i in range(n): # print(mg[i])def cheak():return sum(north)+sum(west) def dfs(x,y,l):if x==n-1 and y==n-1 and cheak()==2:#print(11)print(*(l+[mg[n-1][n-1]]))returnvis[x][y]=1west[x] -= 1north[y] -= 1for dx,dy in [(1,0),(-1,0),(0,1),(0,-1)]:nx=dx+xny=dy+yif 0<=nx<n and 0<=ny<n and vis[nx][ny]!=1 and west[nx]>=1 and north[ny]>=1:dfs(nx,ny,l+[mg[x][y]])vis[x][y]=0west[x] += 1north[y] += 1 dfs(0,0,[])四階幻方
mg=[0]*16 vis=[0]*17 mg[0]=1 vis[1]=1 cnt=0 def dfs(c):global cntif c>=4 and mg[0]+mg[1]+mg[2]+mg[3]!=34:returnif c>=8 and mg[4]+mg[5]+mg[6]+mg[7]!=34:returnif c>=12 and mg[8]+mg[9]+mg[10]+mg[11]!=34:returnif c>=13 and (mg[0]+mg[4]+mg[8]+mg[12]!=34or mg[3]+mg[6]+mg[9]+mg[12]!=34):returnif c>=14 and mg[1]+mg[5]+mg[9]+mg[13]!=34:returnif c>=15 and mg[2]+mg[6]+mg[10]+mg[14]!=34:returnif c>=16 and (mg[12]+mg[13]+mg[14]+mg[15]!=34or mg[3]+mg[7]+mg[11]+mg[15]!=34or mg[0]+mg[5]+mg[10]+mg[15]!=34):returnif c==16:print(cnt)cnt+=1returnfor i in range(2,17):if vis[i]!=1:mg[c]=ivis[i]=1dfs(c+1)mg[c]=0vis[i]=0 # dfs(1) # print(cnt) print(416)分考場
超時(shí)
def dfs(x,room):global num,pif room>=num:#剪枝returnif x>n:num=min(room,num)#更新最優(yōu)解returnfor j in range(1,room+1):#枚舉考場,把第x個(gè)人放到第i個(gè)考場里面k=0#第k個(gè)座位while p[j][k] and a[x][p[j][k]]==0:#如果k位子有人而且不認(rèn)識xk+=1#下一個(gè)位子if p[j][k]==0:p[j][k]=x#第j個(gè)考場的第k個(gè)位子讓第x個(gè)學(xué)生坐dfs(x+1,room)#繼續(xù)p[j][k]=0#回溯p[room+1][0]=x#如果1-room的考場都不能坐,就到第room+1個(gè)考場的第一個(gè)位子dfs(x+1,room+1)p[room+1][0]=0#回溯n=int(input()) m=int(input()) num=110 a=[[0 for j in range(n+1)]for i in range(n+1)]#關(guān)系表 p=[[0 for j in range(n+1)] for i in range(n+1)]#考場狀態(tài) for i in range(m):u,v=map(int,input().split())a[u][v]=1a[v][u]=1#表示x和y認(rèn)識 dfs(1,0) print(num)填字母游戲
n=int(input()) def dfs(s,n):if s in dis.keys():#如果狀態(tài)s在dis中出現(xiàn)過,直接返回其值return dis[s]# 因?yàn)槭切∶飨认缕?#xff0c;故先判斷棋面是否能直接得出結(jié)果if "*OL" in s or 'LO*' in s or 'L*L' in s:dis[s] = 1#將此狀態(tài)加入dis中并幅值return 1if 'LOL' in s:dis[s] = -1return -1if '*' not in s and 'LOL' not in s:dis[s] = 0return 0#無法直接得出結(jié)果的話,flag=-1#如果下一步的所有操作都沒有1或是0,則必定輸for i in range(n):#對每個(gè)位置進(jìn)行遍歷,即下一步的所有可能操作l = list(s)#修改需先轉(zhuǎn)換為列表if l[i]=='*':#可以修改l[i]='L'#若修改為Lr=dfs(''.join(l),n)#結(jié)果需要下一次遞歸得到if r==-1:#因?yàn)樾∶飨认碌?#xff0c;故進(jìn)入下一次循環(huán)后則為k下,若返回值r=-1,說明k輸了,則小明嬴幅值為1dis[s]=1return 1#在每次遞進(jìn)時(shí),如果有小明為1的情況則提前結(jié)束,直接遞歸,沒必要繼續(xù)遞進(jìn)下去if r==0:#k平局,說明小明也是平局,flag賦值為0flag=0#if r==1:對于為什么沒有這種判斷情況,我們可以想想之前for循環(huán)是遍歷下一步的所有走法,#假設(shè)即出現(xiàn)0又出現(xiàn)-1我們?nèi)∈裁唇odis[s]呢?答案是肯定的,我們應(yīng)該取0,即選擇持平的走法,而不是-1,#如果寫上這個(gè)判斷,for循環(huán)最后一次如果是-1,那么前面無論有沒有0,結(jié)果都是-1,這個(gè)答案肯定是錯(cuò)誤的l=list(s)l[i]='O'#若修改為O同理r=dfs(''.join(l),n)if r==-1:dis[s]=1return 1if r==0:flag=0dis[s]=flagreturn dis[s]for i in range(n):s=input()dis={}#字典可以滿足查重,并返回值print(dfs(s,len(s)))?取球博弈
題目思路類似填字母游戲,難點(diǎn)在于狀態(tài)保存和查重,因?yàn)殡p方拿球會誕生出三個(gè)袋子,即兩個(gè)放球的袋子和一個(gè)拿球的袋子狀態(tài),故不能向填字母一樣,填字母可以看成都作用在一個(gè)袋中,所以正反手時(shí),需要轉(zhuǎn)換傳入的順序,因?yàn)槠鏀?shù)都可以用%2=1表示,偶數(shù)都可以用%2=0表示,故可以減小數(shù)組的空間。
注意:本題必須要用這三個(gè)袋子來表示狀態(tài),缺一不可。
這道題很迷,做出來也不是很懂
def dfs(num,a,b):if dis[num][a][b]!='':return dis[num][a][b]if num<min(n1,n2,n3):if a==b:dis[num][a][b]='0'return '0'if a==1:dis[num][a][b] = '+'return '+'else:dis[num][a][b] = '-'return '-'flag='-'for i in [n1,n2,n3]:if num>=i:res=dfs(num-i,b,(a+i)%2)if res=='-':dis[num][a][b]='+'return '+'if res=='0':flag='0'dis[num][a][b]=flagreturn dis[num][a][b]n1,n2,n3=map(int,input().split()) nums=list(map(int,input().split())) for i in range(5):n=nums[i]dis = [[['']*2 for j in range(2)] for i in range(n+1)]print(dfs(n,0,0),end=' ')機(jī)器人塔
A,B=map(int,input().split()) n=A+B t=0 for i in range(n):if i*(i+1)//2==n:t=ibreak # print(t) def dfs(s,numa,numb,temp):if numa<0 or numb<0:#不合法return Falseif temp==0:return numa==numb==0 #都等于0說明合法b=bin(s)[2:].count('1') #規(guī)定二進(jìn)制形式0b0001中1的個(gè)數(shù)就是b的個(gè)數(shù)#注意,多余的0會被省去,所以只能先求出1的數(shù)量a=temp-b#第temp層就有temp個(gè)數(shù)ns=(s^(s>>1))&((1<<(temp-1))-1) #異項(xiàng)得A,同向得B,與異或運(yùn)算相似,#可以用二進(jìn)制的異或運(yùn)算來得出第temp-1層的狀態(tài)ns#而s右移一位后與s異或就是s相鄰異或的結(jié)果,因?yàn)榈贸龅慕Y(jié)果還是temp個(gè)位數(shù),#所以將其與其長度為tmep-1的011111想與,從而得到第temp-1層的數(shù)return dfs(ns,numa-a,numb-b,temp-1)cnt=0 for i in range(1<<t):#最低層確定后,整個(gè)塔就確定了if dfs(i,A,B,t):#所以對底層的每一個(gè)狀態(tài)都dfs確定是否合法cnt+=1 print(cnt)總結(jié)
- 上一篇: 秒建炫酷的开源项目文档,这款神器用起来够
- 下一篇: 奇数值结点链表