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