【每日一題】(31題)面試官:你對圖論了解多少?(一)

2021-03-02 松寶寫代碼

關注「松寶寫代碼」

精選好文,每日一題

作者:Overstarshttps://shuangxunian.gitee.io/2020/08/24/graphTheory/

2020,實「鼠」不易

2021,「牛」轉乾坤

風勁潮湧當揚帆,任重道遠須奮蹄!

一、前言

今天我們約來了 Overstars的圖論總結,ACM打比賽,他的方向是思維數論圖論。下文是他和他隊友四年刷圖論的總結。其實本身就是個筆記,以後方便打板子的。我們把整個總結分為多篇文章來發,歡迎關注「松寶寫代碼」每日一題。

原文地址:https://shuangxunian.gitee.io/2020/08/24/graphTheory/

2020.12.23 立的 flag,每日一題,題目類型不限制,涉及到JavaScript,Node,Vue,React,瀏覽器,http,算法等領域。

本文是:【每日一題】(31題)面試官:你對圖論了解多少?(一)

二、圖論

作者 Overstars

https://shuangxunian.gitee.io/2020/08/24/graphTheory/

1、常見概念

簡單圖:不含有平行邊和環的圖。

多重圖:含有平行邊(同向)的圖。

完全圖:每一對節點之間都有邊的簡單圖,$n$個節點無向完全圖記為$K_n$。

平面圖:能將所有點和邊畫在平面上,且邊與邊不相交的無向圖。

補圖:由$G$中所有節點和所有能使$G$成為完全圖的添加邊組成的圖。

生成子圖(Spanning Subgraph):含有原圖$G$中所有結點的子圖。

圖同構的必要條件

節點數相同。

邊數相同。

度數相同的結點數相同。

網絡流常用概念

連通圖:只有一個連通分支(極大連通子圖)的圖。

割點:無向連通圖中一個頂點$v$,,若刪除它以及關聯的邊後,得到的新圖至少包含兩個連通分支。

橋:無向連通圖中的一條邊$e$,刪除它得到的新圖包含兩個連通分量。

團(Clique):原圖$G$的一個完全子圖。

極大團(Maximal Clique):不是其它團的子圖的團。

最大團(Maximum Clique):頂點最多的極大團。

誘導子圖(Induced Subgraph):所有節點在原圖$G$上連接的邊都被包含在內的子圖。

獨立集:在圖上選出一些節點,這些節點間兩兩無邊的點集。

路徑覆蓋:在圖中找一些路徑,這些路徑覆蓋圖中所有的頂點,每個頂點都只與一條路徑相關聯。

最小染色數:用最少的顏色個數給點染色且任意兩點相鄰點顏色不同,最少的顏色個數。

弦圖常用概念

弦:連接環上兩個不相鄰節點的邊。

弦圖:圖上任意一個點數大於三的環都至少存在一條弦的無向圖。

單純點:節點$v$與相鄰節點的誘導子圖是一個團。

完美消除序列:有$n$個點的排列$v1,v2,\dots,vn$,該排列滿足$vi$在${vi,v{i+1},\dots,v_n}$的誘導子圖中是一個單純點。

一個無向圖是弦圖若且唯若它有一個完美消除序列

2、常見結論

每個圖中節點度數和等於邊數的二倍,$\sum\limits_{v\in V} deg(v)=2\left| E \right|$。

任何圖中度數為奇數的節點必定有偶數個。

完全圖$K_n$的邊數$=\frac{n(n-1)}{2}$

最大團中頂點數量 = 補圖的最大獨立集中頂點數量

最大團數 ≤ 最小染色數,最大獨立集 ≤ 最小團覆蓋

弦圖中:最大團數 = 最小染色數,最大獨立集 = 最小團覆蓋

平面圖邊數$m\le 3n-6$。

git config --global core.autocrlf false和git config --global core.safecrlf true還有git init

3、弦圖最大勢算法求消除序列並判定弦圖

判斷一個消除序列是否為完美消除序列:從後向前枚舉序列中的點$vi$,設序列中在$vi$後且與$vi$相鄰的點集依次為${v{j1},v{j2},\dots,v{jk}}$,判斷$vj1$是否與${v{j2},\dots,v_{jk}}$相鄰即可。

#include<bits/stdc++.h>

using namespace std;

const int maxn=1005;

struct edge

{

int v,nex;

edge(int v=0,int n=-1):

v(v),nex(n){}

} e[maxn*maxn];

int cnt=0,head[maxn];

inline void add(int u,int v)

{

e[++cnt].v=v;

e[cnt].nex=head[u];

head[u]=cnt;

}

int label[maxn],id[maxn],order[maxn];//id[i]表示節點i在序列中的編號

bool vis[maxn];//order[i]為完美消除序列第i個節點,label[x]表示x點與多少個已標號節點相鄰

vector<int> vec[maxn];//vec[x]存儲*與x個已標號節點相鄰*的節點

void mcs(int n)//節點數量

{//複雜度O(n+m)

memset(vis,0,sizeof(vis));

memset(label,0,sizeof(label));

for(int i=1;i<=n;i++)

vec[0].push_back(i);

int maxx=0,now=0;

for(int i=1;i<=n;i++)

{//從前往後,每輪必定會找出一個

bool flag=0;

while(!flag)

{

for(int j=vec[maxx].size()-1;j>=0;j--)

{//從後往前找

if(vis[vec[maxx][j]])//該節點已經標記過

vec[maxx].pop_back();

else{

flag=1;//找到個未訪問的節點

now=vec[maxx][j];

break;

}

}

if(!flag)

maxx--;

}

vis[now]=1;//

order[n-i+1]=now;

id[now]=n-i+1;//節點x在序列中的位置

for(int j=head[now];~j;j=e[j].nex)

{//遍歷與now節點相鄰的節點

int v=e[j].v;

if(!vis[v])

{

label[v]++;//v節點與已標號節點數++

vec[label[v]].push_back(v);

maxx=max(maxx,label[v]);//找出最大的那個節點

}

}

}

}

int bucket[maxn];

bool isperfect(int n)

{//複雜度O(n+m)

mcs(n);//計算消除序列並判斷是否為完美消除序列

memset(vis,0,sizeof(vis));//判斷序列中的點v_i是否與其後所有點相接

for(int i=n;i>0;i--)//序列從後向前

{

int tot=0,ret=1;//每輪桶清空

for(int j=head[order[i]];~j;j=e[j].nex)

if(id[e[j].v]>i)//排在序列編號x後面與x相鄰的點集記為:N(x)

vis[bucket[++tot]=e[j].v]=1;//序列中x後且與x鄰接點標記並放入桶中

for(int j=head[bucket[1]];~j;j=e[j].nex)//buc[1]的id為N(x)中最小?

{//bucket[1]=0...

int v=e[j].v;

if(v!=bucket[1]&&vis[v])

{//判斷N(x)中排在最前面的點是否與N(x)其他點相鄰

ret++;

}

}

for(int j=1;j<=tot;j++)

vis[bucket[j]]=0;//防止每次memset超時

if(tot&&ret!=tot)//不全部鄰接

return 0;

}

return 1;

}

int main()

{

int n,m,u,v;

while(cin>>n>>m&&n&&m)

{

memset(head,-1,sizeof(head));

memset(order,0,sizeof(order));

cnt=0;

for(int i=1;i<=n;i++)

vec[i].clear();

for(int i=1;i<=m;i++)

{

cin>>u>>v;

add(u,v);

add(v,u);

}

cout<<(isperfect(n)?"Perfect\n\n":"Imperfect\n\n");

}

return 0;

}

4、三元環步驟

對原無向圖進行定向,對任何一條邊,從度數大的點連向度數小的點,如果度數相同,從編號小的點連向編號大的點。得到一個有向無環圖。

枚舉每一個節點$u$,將$u$所有相鄰節點打上$u$的標記。再枚舉$u$的相鄰節點$v$,枚舉$v$的相鄰節點的相鄰節點$w$,如果$w$上有被標記為$u$,則$(u,v,w)$就是一個三元環。

統計圖上三元環數量,複雜度$O(m\sqrt{m})$。

vector G[maxn];//新圖

int vis[maxn],deg[maxn];

int cycle3(int n)

{

int ans=0;

for(auto &e:edges)

{//統計原無向圖度數

deg[e.u]++;

deg[e.v]++;

}

for(auto &e:edges)//建立新圖

if(deg[e.u]<deg[e.v]||(deg[e.u]==deg[e.v]&&e.u<e.v))

G[e.u].push_back(edge(e.u,e.v));

else

G[e.v].push_back(edge(e.v,e.u));

for(int u=1;u<=n;u++)

{

for(int v:G[u])

vis[v]=u;//相鄰節點打上u的標記

for(int v:G[u])

for(int w:G[v])

if(vis[w]==u)

ans++;

}

return ans;

}

5、最短路Dijkstra堆優化

複雜度$O(ElgE)$,稠密圖中有時不如普通版優秀,但比SPFA快。

const int MAXN=1050;

struct qnode{

int v,c;

qnode(int _v=0,int _c=0):v(_v),c(_c){}

bool operator <(const qnode &r)const{

return c>r.c;//重載運算符<

}

};

struct Edge{

int v,cost;

Edge(int _v=0,int _cost=0):v(_v),cost(_cost){}

};

vector<Edge>E[MAXN];//使用前必須清空0~n

bool vis[MAXN];

int dist[MAXN];//到這個點的最近隊員的

void Dijkstra(int n,int start)//傳入總數及起點

{//點的編號從 1 開始

memset(vis,false,sizeof(vis));

for(int i=1;i<=n;i++)

dist[i]=inf;

priority_queue<qnode>que;

while(!que.empty())

que.pop();

dist[start]=0;

que.push(qnode(start,0));

while(!que.empty())

{

qnode now=que.top();

que.pop();

int u=now.v;

if(vis[u])

continue;

vis[u]=true;

for(int i=0;i<E[u].size();i++)

{

int v=E[u][i].v;

int cost=E[u][i].cost;

if(!vis[v]&&dist[v]>dist[u]+cost){

dist[v]=dist[u]+cost;

que.push(qnode(v,dist[v]));

}

}

}

}

void addedge(int u,int v,int w)

{

E[u].push_back(Edge(v,w));

}

SPFA

最壞複雜度$O(VE)$,V為節點數,不適用於稠密圖

檢測負環:當存在一個點入隊大於等於$V$次,則有負環

BFS實現(求最短路)

const int inf=0x3f3f3f3f,MAXN=5051;

struct node

{

int v,w,next;

} e[MAXN];

int cnt,dist[MAXN],head[MAXN],num[MAXN];

bool vis[MAXN];

void add(int u,int v,int w)//鏈式前向星存圖,無向則雙向添加

{

e[cnt].v=v;//邊的結尾節點

e[cnt].w=w;

e[cnt].next=head[u];//去找以u為起始的上一個節點(相當於鍊表,起始為-1)

head[u]=cnt++;//保存該邊(最後的邊)在e[i]中的編號

}

bool SPFA(int n,int x)//節點數量n,起點編號x

{

memset(dist,inf,sizeof(dist));

memset(vis,0,sizeof(vis));

memset(num,0,sizeof(num));

dist[x]=0;//該題起點任意

num[x]++;//入隊次數++

queue<int> QAQ;

QAQ.push(x);

while(!QAQ.empty())

{

int now=QAQ.front();

QAQ.pop();

vis[now]=0;//從隊列中取出

for(int i=head[now];i!=-1;i=e[i].next)

{//遍歷以now開頭的所有邊,嘗試以x->now->to鬆弛x->to

int to=e[i].v;//嘗試鬆弛x->to的當前距離

if(dist[to]>dist[now]+e[i].w)

{

dist[to]=dist[now]+e[i].w;//成功用x->now->to鬆弛x->to

if(!vis[to])//x->to被成功鬆弛且to不在隊列

{

vis[to]=1;//標記to加入隊列

QAQ.push(to);

num[to]++;//to加入隊列次數++

if(num[to]>n)

return 1;//有負權迴路,無法求最短路徑

}

}

}

}

return 0;

}

DFS優化(負環檢測)

請先用前向星加邊。因為圖有可能不連通,檢測負環要枚舉每個起點。

bool spfa(int x)//DFS優化

{

vis[x]=1;

for(int i=head[x];~i;i=e[i].next){

int v=e[i].v;

if(dist[v]>dist[x]+e[i].w)//求最短路

{

dist[v]=dist[x]+e[i].w;

if(vis[v])//存在一點在一條路徑上出現多次,存在負權環

return 0;

if(!spfa(v))//搜到了存在負權環

return 0;

}

}

vis[x]=0;

return 1;//未找到負權環

}

Floyd

long long path[805][805];

void floyd(int n)//節點編號1~n

{

for(int k=1;k<=n;k++)

for(int i=1;i<=n;i++)

for(int j=1;j<=n;j++)

if(path[i][k]+path[k][j]<path[i][j])

{//當i,j的原來的邊的最短距離,大於經過k點所到達的距離就替換

path[i][j]=path[i][k]+path[k][j];

}

}

謝謝支持

1、文章喜歡的話可以「分享,點讚,在看」三連哦。

2、作者暱稱:saucxs,songEagle,松寶寫代碼。「松寶寫代碼」公眾號作者,每日一題,實驗室等。一個愛好折騰,致力於全棧,正在努力成長的字節跳動工程師,星辰大海,未來可期。內推字節跳動各個部門各個崗位。

3、長按下面圖片,關注「松寶寫代碼」,是獲取開發知識體系構建,精選文章,項目實戰,實驗室,每日一道面試題,進階學習,思考職業發展,涉及到JavaScript,Node,Vue,React,瀏覽器,http等領域,希望可以幫助到你,我們一起成長~

字節內推福利

回復「校招」獲取內推碼

回復「社招」獲取內推

回復「實習生」獲取內推

後續會有更多福利

學習資料福利

回復「算法」獲取算法學習資料

往期「每日一題」1、JavaScript && ES62、瀏覽器3、Vue4、HTML55、算法6、Node7、Http

PS:閱讀原文看代碼更清爽

相關焦點

  • 【每日一題】(33題)面試官:你對圖論了解多少?(三)
    【每日一題】(33題)面試官:你對圖論了解多少?
  • [每日一題]面試官問:for in和for of 的區別和原理?
    關注「松寶寫代碼」,精選好文,每日一題時間永遠是自己的每分每秒也都是為自己的將來鋪墊和增值作者:saucxs
  • 每日一題:Excel統計不重複數據的方法!
    如下圖:是各商場銷量統計表,現在想要計算每個商場有多少銷售人員,及每個商場有多少銷量,注意人員有重複。
  • 北媽每日一題:JS從無序亂碼找我要的數字!
    點擊上方「前端你別鬧」,關注並星標喜歡我的都關注我了北媽每日一題
  • LeetCode 每日一題(順帶吹水聊聊未來)
    2020-7-20 00:35:07寫完小冊,看到每日一題刷新了,【簡單】難度,那就順帶刷了,再順帶和小夥伴們吹吹水吧~題目 給定一個已按照升序排列 的有序數組,找到兩個數使得它們相加之和等於目標數。你可以假設每個輸入只對應唯一的答案,而且你不可以重複使用相同的元素。示例:輸入: numbers = [2, 7, 11, 15], target = 9輸出: [1,2]解釋: 2 與 7 之和等於目標數 9 。因此 index1 = 1, index2 = 2 。
  • 每日一題:Excel多條件查找最大或最小值的公式!
    每日一題:微課系列課程10,開始開課了!
  • 每日一題:Excel怎麼計算庫存數據呢?
    1、方法一:一個單元格一個單元格計算,相信很多人是這麼計算的。這個錄入公式很麻煩,容易出錯,一旦刪除一列數據後,公式還出錯。函數可以設置很多組條件,進行複雜計算,參考1月14日文章:每日一題:Excel多條件合計函數SUMIFS()應用實例。1、購買一本書:Excel2016或2013隨便買一本就行。
  • 面試常掛題:你最大的缺點是什麼?
    破解之道所有的面試問題的破解之道都在於換位思考,即:如果你是面試官,你想通過這題問什麼?看出來什麼?退一步來想,你是如何知道自己的缺點的呢?歷史做得不好,拿什麼讓別人相信你未來能做好呢?哪怕我工作一年之後再申請 Citadel 的時候,獵頭都跟我說要求 GPA 多少以上。項目經驗不足是實話,學生做的項目就是和工業界的標準不一樣,所以這個缺點是客觀存在的,無可厚非的。
  • 恕我直言,階乘相關的面試題你還真不一定懂!
    對於如何算 n 的階乘,只要你知道階乘的定義,我想你都知道怎麼算,但如果在面試中,面試官拋給你一道與階乘相關,看似簡單的算法題,你還真不一定能夠給出優雅的答案!本文將分享幾道與階乘相關的案例,且難度遞增。案例一給定一個整數 N,那麼 N 的階乘 N! 末尾有多少個 0?例如: N = 10,則 N!= 3628800,那麼 N! 的末尾有兩個0。
  • 10年+技術總監面FB,面試官竟然讓我寫算法題?
    比如這個快排(Quick Sort)模板,直接能讓面試官眼前一亮👇面對激烈的求職,想要在面試中甩開對手的最好辦法,就是提高代碼質量,拿一道FB經常考的「最長回文串」舉例:描述:給出一個字符串(假設長度最長為1000),求出它的最長回文子串,你可以假定只有一個滿足條件的最長回文串。
  • 每日一練 | Data Scientist & Business Analyst & Leetcode 面試題 1043
    Data Application Lab 自2017年6月15日起,每天和你分享討論一道數據科學(DS)和商業分析(BA)領域常見的面試問題。
  • 【每日一練】1011-公共基礎題
    下列情況可以:一、遇有特殊重大緊急情況;二、經多次請示直接上級,長期未得到解決的重大問題;三、上級領導或領導機關交辦,並指定越級直接上報的事項;四、對直接上級機關或領導進行檢舉、控告;五、直接上下級機關有爭議,而無法解決的重大問題;六、詢問、聯繫無需經過直接上級機關的一些工作問題等。七、在市場經濟的今天,為使文件精神儘快與群眾見面,以便更好的貫徹執行,採用電視、電腦、電話、廣播、報刊等方式行文。
  • 55個必考金融面試題,面試官親自發答案啦!
    >我以為的優秀答案是醬紫的👇【當過面試官的在職前輩給出的回答】對比兩個答案,你能得出什麼結論呢?所以針對面試真題,參考各種資料形成自己的思考固然重要,更重要的是了解面試官的腦迴路,畢竟面試官才是那個決定是否給你發offer的人。對很多同學來說,有的時候「網絡上太多繁雜的面經」已經成為了一種負擔(不僅整理需要時間,內容質量也不可靠);找師兄師姐問呢?各種經驗其實也跟網絡面經,一般沒有答案,只有題型+主觀的做題心得。
  • 每日一練 | Data Scientist & Business Analyst & Leetcode 面試題 979
    Data Application Lab 自2017年6月15日起,每天和你分享討論一道數據科學(DS)和商業分析(BA)領域常見的面試問題。
  • 每日一練 | Data Scientist & Business Analyst & Leetcode 面試題 980
    Data Application Lab 自2017年6月15日起,每天和你分享討論一道數據科學(DS)和商業分析(BA)領域常見的面試問題。
  • 女面試官:一包煙抽掉三根,還剩多少?帥哥回答17根當場被拒!
    哈嘍,小編又上線了,不知道你們當中有多少人馬上就要面臨高考了呢,沒有面臨高考的小夥伴們也不用擔心,即便是距離大學很遠,恐怕你的人生當中也會面臨很多很多的面試,比如學生會又或者是實習找工作。所以提前做好準備也是一個很好的選擇呀,今天小編帶給大家的問題就是若有一位女面試官問你們:一包煙抽掉三根還剩多少?這個問題問到你們會如何作答呢?接下來小編就帶大家分析一下。
  • 7 個沙雕又帶有陷阱的 JS 面試題
    在 JS 面試中,經常會看到一些簡單而又沙雕的題目,這些題目包含一些陷阱,但這些在我們規範的編碼下或者業務中基本不會出現。有些面試官就是這樣,不專注於制定代碼的標準和規範上,卻用不規範的代碼去檢驗別人是否細心。這魔幻的世界就是一個攀比優越感的,我能考你,我就是比你優越,真實。來看看這 7 個沙雕題目是哪些。1.
  • 每日一題:VLOOKUP實現數據列轉行的方法!
    1、購買一本書:Excel2016或2013隨便買一本就行。5、在公眾號回覆:下載地址,下載每日一題的全部示例,直接就可以做練習了。
  • 每日一練 | Data Scientist & Business Analyst & Leetcode 面試題 1044
    Data Application Lab 自2017年6月15日起,每天和你分享討論一道數據科學(DS)和商業分析(BA)領域常見的面試問題。
  • 「偷師」FB面試官發現:刷題超過300,再多刷也沒意義!
    九章的令狐衝老師曾經說過:將核心、高頻的題目精刷200~300題就足以應付大廠面試了。再往多了意義並不大!考的不考的都刷,完全是浪費時間。備戰算法性價比最高方法是:按照算法的考察頻率,刷夠刷透相應的題量。哪些是常考算法,以及我到底刷到多少才算夠呢?