搜索(八皇后) 洛谷P1219:
//搜索的實(shí)現(xiàn)。 //優(yōu)化dfs1。 //八皇后。 //n個(gè)皇后在不在同一條直線或同一條斜線,一共多少方案。 #include<iostream> #include<cstdio> using namespace std; int n,ans=0; int pos[maxn];//pos[i]代表第i行的皇后放在了第pos[i]列。 void dfs(int now){//現(xiàn)在要去嘗試放第now行的皇后。 if(now>n){ ans++; return; } for(int j=1;j<=n;j++){//第now行的皇后放在第j列。 bool able=true; for(int i=1;i<now;i++){//枚舉第i行的皇后。 if(pos[i]=j||abs(now-i)==abs(j-pos[i])){ able=false; break; } } if(able){ pos[now]=j; dfs(now+1); } } } int main(){ n=13;//3.68s。 dfs(1); cout<<ans<<endl; return 0; }
優(yōu)化dfs2。
判斷第i行、第i條斜線上有沒有皇后。
斜線=i+j-1>。
#include<iostream> #include<cstdio> using namespace std; int n,ans=0; int pos[20];//pos[i]代表第i行的皇后放在了第pos[i]列。 bool col[20];//col[i] 代表第i條的直線有沒有皇后。 bool xie1[50];//xie[1] 代表第i條從左上到右下的斜線有沒有皇后。 bool xie2[50];//xie[2] 代表第i條從右上到左下的斜線有沒有皇后。 void dfs(int now){//現(xiàn)在要去嘗試放第now行的皇后。 if(now>n){ ans++; return; } for(int j=1;j<=n;j++){//第now行的皇后放在第j列。 int ibx1=j-now+n;//從左上到右下的斜線編號(hào)。 int ibx2=now+j-1;//從右上到左下的斜線編號(hào)。 if(!col[j]&&!xie1[idx1]&&!xie2[idx2]){ pos[now]=j; col[j]=xie1[idx1]=xie2[idx2]=true; dfs(now+1); col[j]=xie1[idx1]=xie2[idx2]=false; } } } int main(){ n=13;//0.6141s。 n=14;//3.494s。 dfs(1); cout<<ans<<endl; return 0; }
圖論:
圖的概念 由點(diǎn)和邊構(gòu)成的元素
邊:如果邊都有方向 我們叫它有向圖 沒方向叫無向圖
一、圖的一些基本概念:
1.度:一個(gè)頂點(diǎn)連了幾條邊 就是它多少度
2.有向圖里的入度和出度:連向自己的度就是入度 往外連得就是出度
3.有向圖里的自環(huán):既是入度又是出度
4.路徑:只要沿著邊走叫做路徑 如:1 -> 2 -> 3 -> 2 -> 1
5.簡(jiǎn)單路徑:不能走重復(fù)點(diǎn)和邊的路徑 如:1 -> 2 -> 3u
6.環(huán):起點(diǎn)等于終點(diǎn)的路徑(繞起來) 如:1 -> 2 -> 1
7.簡(jiǎn)單環(huán):起點(diǎn)等于終點(diǎn)且保證除起點(diǎn)和終點(diǎn)外不經(jīng)過重復(fù)的點(diǎn)和邊
圖的分類方式:1.樹:無向、無環(huán)、連通(任意兩個(gè)點(diǎn)之間都可以互相走到)
如果有一個(gè)n個(gè)點(diǎn)的樹,它一定會(huì)有n - 1條邊
2. 森林:無向、無環(huán)(很多棵樹)形象!
3.有向樹:無環(huán)、連通
{
外向樹:如果所有邊都指向外邊
內(nèi)向樹:如果所有邊都指向一點(diǎn)
注意:有可能一棵樹既是外向樹又是內(nèi)向樹!
}
4.章魚圖:無向、連通、有環(huán)且只有一環(huán)
章魚圖的每一個(gè)點(diǎn)向往外延伸 一定是一棵樹。如果一個(gè)章魚圖有n個(gè)點(diǎn),它就一定有n條邊
章魚圖想要變成樹,只需要去掉環(huán)上的一條邊
一棵樹想要變成章魚圖,只需加一條邊
5.DAG ——> 有向無環(huán)圖
6.二分圖(無向圖) --> 能夠把所有的邊分為兩部分不要求聯(lián)通
見圖:

樹和森林一定是二分圖
判斷:沒有奇環(huán)(即奇數(shù)個(gè)頂點(diǎn)組成的環(huán)) 就是二分圖
二、代碼實(shí)現(xiàn)存圖
1.鄰接矩陣存圖
int z[101][101]; //z[i][j] 代表i到j(luò)那條邊的長(zhǎng)度 int main(){ cin >> n >> m; for(int i = 1; i <= n; i++){ int s,e,d; cin >> s >> e >> d;//起點(diǎn)為s,終點(diǎn)為e,長(zhǎng)度為d z[s][e] = d;//有向圖 //z[e][s] = d; 無向圖 } }
壞處:內(nèi)存消耗比較大
好處:簡(jiǎn)潔、快(因?yàn)橹灰纈到j(luò)的那條邊的信息 O(1))
2.邊表存圖
vector< pair<int,int> > z[maxn]; //z[i]:代表 所有以i為起點(diǎn)的所有邊 //z[i][j] :代表所有以i為起點(diǎn)的第j條邊 //z[i][j].forst:代表i為起點(diǎn)的第j條邊的終點(diǎn) //z[i][j].second: 代表i為起點(diǎn)的第j條邊的起點(diǎn) void add_edge(int s, int e, int d){ z[s].push_back(make_pair(e,d)); //push_back:向vector的末尾添加一個(gè)元素 } int main(){ cin >> n >> n; for(int i = 1; i <= n; i++){//添加一條從s出發(fā),到e 長(zhǎng)度為d的邊 int s,e,d; cin >> s >> e >> d; add_edge(s,e,d);//添加一條邊 STL函數(shù) add_edge(e,s,d);//無向圖 } for(int i = 1; i <= n; i++) for(int j = 0; j < z[i].size();j++){ int e = z[i][j].first; int d = z[i][j].second; //代表當(dāng)前的邊 是從i開始的第j條邊 終點(diǎn)為e 長(zhǎng)度為d } }
優(yōu)點(diǎn):改善了上一種方法的內(nèi)存
缺點(diǎn):慢
三、解決一些問題
1.一張圖 有n個(gè)點(diǎn) me 條邊 并且是一張無向圖 請(qǐng)判斷是否是二分圖(二分圖染色)
思路:染色法從一號(hào)點(diǎn)開始 將其染色為1 與它相鄰的點(diǎn)應(yīng)當(dāng)是第二部分 ;與第二部分相鄰的點(diǎn)就是第一部分。如果能夠成功將所有的點(diǎn)都染色 就是二分圖 如果一個(gè)點(diǎn)的兩邊的點(diǎn)顏色相同就不是二分圖
注意:這里其實(shí)有一個(gè)坑,我們從一號(hào)點(diǎn)開始 如果我們的圖不連通就無法找到所有點(diǎn),所以需要枚舉
vector<int > z[maxn]; //z[i]:代表 所有以i為起點(diǎn)的所有邊 //z[i][j] :代表所有以i為起點(diǎn)的第j條邊 //z[i][j].forst:代表i為起點(diǎn)的第j條邊的終點(diǎn) //z[i][j].second: 代表i為起點(diǎn)的第j條邊的起點(diǎn) void add_edge(int s, int e){ z[s].push_back(e); //push_back:向vector的末尾添加一個(gè)元素 } int col[maxn]; //col[i] == 0 代表還沒有染色 //否則代表第i個(gè)點(diǎn)的顏色 int main(){ cin >> n >> n; for(int i = 1; i <= n; i++){//添加一條從s出發(fā),到e 長(zhǎng)度為d的邊 int s,e; cin >> s >> e; add_edge(s,e);//添加一條邊 STL函數(shù) add_edge(e,s);//無向圖 } for(int i = 1; i <= n; i++){ if(col[i] != 0) continue; col[1] = 1; queue<int> q;//可以改變周圍點(diǎn)顏色的點(diǎn) q.push(1); while(q.size()){ int i = q.front(); q.pop(); for(int j = 0; j < z[i].size(); j++){ int k = z[i][j]; //i -> k 的一條邊 if(!col[k]){ col[k] = 3 - col[i]; q.push(k); } else{ if(col[k] !== col[i]){ cout << "不是二分圖!" << endl; exit(0); } } } } } cout << "是二分圖 !"<< endl;//順利完成整個(gè)程序 return 0; }
2.拓?fù)渑判颍ㄡ槍?duì)DAG)
保證排完序之后原圖當(dāng)中存在的邊都是從左向右指的,叫做拓?fù)渑判颉?/strong>
如果一個(gè)點(diǎn)的入度為零 應(yīng)把它放在最前面 把每次入讀為0的點(diǎn)更新實(shí)現(xiàn):
vector<int > z[maxn]; //z[i]:代表 所有以i為起點(diǎn)的所有邊 //z[i][j] :代表所有以i為起點(diǎn)的第j條邊 //z[i][j].forst:代表i為起點(diǎn)的第j條邊的終點(diǎn) //z[i][j].second: 代表i為起點(diǎn)的第j條邊的起點(diǎn) void add_edge(int s, int e){ z[s].push_back(e); //push_back:向vector的末尾添加一個(gè)元素 } int col[maxn]; //col[i] == 0 代表還沒有染色 //否則代表第i個(gè)點(diǎn)的顏色 int in[maxn];//代表i點(diǎn)的入度 是多少 int main(){ cin >> n >> n; for(int i = 1; i <= n; i++){//添加一條從s出發(fā),到e 長(zhǎng)度為d的邊 int s,e; cin >> s >> e; add_edge(s,e);//添加一條邊 STL函數(shù) in[e] ++; } int cnt = 0; for(int i = 1; i <= n; i++) if(in[i] == 0) resault[++cnt] = i; for(int i = 1; i <= n; i++){//當(dāng)前要取出的拓?fù)渑判虻慕Y(jié)果中的第i個(gè)點(diǎn) int p = result[i]; for(int j = 0; j < z[p].size(); j++){ int q = z[p][j];//p -> q的邊 in[q] --; if(in[q] == 0) result[++cnt] = q; } } return 0; }
插播一點(diǎn)小概念:每個(gè)節(jié)點(diǎn)在第幾層叫做這個(gè)節(jié)點(diǎn)的深度
祖先:在i上面的節(jié)點(diǎn)
lca(i,j) = i 和j的最近公共祖先
Q;給出樹 求出lca(i,j)
vector<int > z[maxn];
//z[i]:代表 所有以i為起點(diǎn)的所有邊
//z[i][j] :代表所有以i為起點(diǎn)的第j條邊
//z[i][j].forst:代表i為起點(diǎn)的第j條邊的終點(diǎn)
//z[i][j].second: 代表i為起點(diǎn)的第j條邊的起點(diǎn)
void add_edge(int s, int e){
z[s].push_back(e);
//push_back:向vector的末尾添加一個(gè)元素
}
void dfs(int i, int j){//i 的父節(jié)點(diǎn)是j
f[i] = j;
depth[i] = depth[j] + 1;
for(int k = 0; k < z[i].size();k++){
int l = z[i][k];//這條邊是從 i -> l的邊
if(l != j) dfs(l,i);
}
}
void get_lca(int p1, int p2){
while(p1 != p2){
if(depth[p1] < depth[p2]) swap(p1,p2);
p1 = f[p1];
}
return p1;
}
int depth[maxn], f[maxn];//深度 父節(jié)點(diǎn)
int main(){
cin >> n >> n;
for(int i = 1; i <= n; i++){//添加一條從s出發(fā),到e 長(zhǎng)度為d的邊
int s,e;
cin >> s >> e;
add_edge(s,e);//添加一條邊 STL函數(shù)
add_edge(e,s);
}
dfs(1,0);
return 0;
}
優(yōu)化:倍增求lca
int depth[maxn], f[maxn][20];//深度 從i vector<int > z[maxn]; //z[i]:代表 所有以i為起點(diǎn)的所有邊 //z[i][j] :代表所有以i為起點(diǎn)的第j條邊 //z[i][j].forst:代表i為起點(diǎn)的第j條邊的終點(diǎn) //z[i][j].second: 代表i為起點(diǎn)的第j條邊的起點(diǎn) void add_edge(int s, int e){ z[s].push_back(e); //push_back:向vector的末尾添加一個(gè)元素 } void dfs(int i, int j){//i 的父節(jié)點(diǎn)是j f[i][0] = j; for(int k = 1; k < 20; k++){ f[i][k] = f[f[i][k - 1] ][k - 1]; } depth[i] = depth[j] + 1; for(int k = 0; k < z[i].size();k++){ int l = z[i][k];//這條邊是從 i -> l的邊 if(l != j) dfs(l,i); } } void get_lca(int p1, int p2){ //把p1 p2深度調(diào)整成一樣的 else if(depth[p1] < depth[p2]) swap(p1, p2); for(int i = 19; i >= 0; i--) if(depth[f[p1][i]] >= depth[p2]) p1 = f[p1][i]; if(p1 == p2) return p1; for(int i = 19; i >= 0; i--) if(f[p1][i] != f[p2][i]) p1 = f[p1][i], p2 = f[p2][i]; return f[p1][0]; } int main(){ cin >> n >> n; for(int i = 1; i <= n; i++){//添加一條從s出發(fā),到e 長(zhǎng)度為d的邊 int s,e; cin >> s >> e; add_edge(s,e);//添加一條邊 STL函數(shù) add_edge(e,s); } dfs(1,0); return 0; }
浙公網(wǎng)安備 33010602011771號(hào)