加了部分博客鏈接
1.圖的存儲:
鄰接矩陣: g[a][b]存儲點a,b間的有關信息(權值或該兩點間是否有邊)稠密圖
鄰接表: 類似哈希表的拉鍊法,每個點都有一個單鏈表,存儲這個點可以走到的點(包括直接走到和間接走到)。
鄰接表的結構體實現
struct node{
int value;//存儲邊的權值
int to;//存儲該邊的終點
int next;//存儲下一條邊的編號
}a[N];
int cnt=0;
int head[N];//存儲以i爲起點的邊的編號
void add(int u,int v,int value){
a[cnt].to=v;
a[cnt].value=value;
a[cnt].next=head[u];
head[u]=cnt++;//以當前點爲起點的編號爲cnt
//無向圖,正反各存一遍
a[cnt].to=u;
a[cnt].value=value;
a[cnt].next=head[v];
head[v]=cnt++;
}
鏈式前向星:
int h[N],e[N],ne[N],idx;
void init(){
idx=0;
memset(h,-1,sizeof h);
}
void add(int a,int b){
e[idx]=b,ne[idx]=h[a],h[a]=idx,idx++;
}
2.圖的深度優先遍歷和廣度優先遍歷
3.拓撲排序
有向無環圖=拓撲圖
手寫隊列(容易出鍋)
int h[N],e[N],ne[N],idx,ans=N,n,m;
int d[N],q[N];
void init(){
idx=0;
memset(h,-1,sizeof h);
}
void add(int a,int b){
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
bool topsort(){
int hh=0,tt=-1;
for(int i=1;i<=n;i++)
if(!d[i]) q[++tt]=i;
while(hh<=tt){
int t=q[hh++];
for(int i=h[t];i!=-1;i=ne[i]){
int j=e[i];
if(--d[j]==0) q[++tt]=j;
}
}
return tt==n-1;
}
4.最短路算法
最短路問題分爲單源最短路和多源匯最短路。前者是求某一點到其他點的最短距離,後者是起點終點不確定,任選一組起點終點求最短路。
n點m邊
Dijkstra
所有邊權均是正數的單源最短路 O(nn)
適用於稠密圖 即m和nn接近
int n,m;///n個點 m條邊
int g[maxn][maxn];///鄰接矩陣建圖
int dis[maxn];///記錄當前點到起點的距離
bool st[maxn];///若當前點的最短距離已經確定 爲true
int dijkstra(){
memset(dis,0x3f,sizeof dis);///初始化距離爲正無窮
dis[1]=0;///從1開始更新
for(int i=0;i<n-1;i++){
int t=-1;
///找到當前不在st中而且距離最小的點t
for(int j=1;j<=n;j++)
if(!st[j]&&(t==-1||dis[t]>dis[j]))
t=j;
///用點t更新其他不在st中的點的距離
for(int j=1;j<=n;j++)
dis[j]=min(dis[j],dis[t]+g[t][j]);
///將t點放到st集合裏
st[t]=true;
}
///如果該點距離爲正無窮 說明沒有更新過
if(dis[n]==0x3f3f3f3f) return -1;
return dis[n];
}
堆優化的Dijkstra
所有邊權均是正數的單源最短路 O(mlogn)
適用於稀疏圖 即m和n一個數量級
int n,m;
int h[maxn],w[maxn],e[maxn],ne[maxn],idx;
int dis[maxn];
bool st[maxn];
void add(int a,int b,int c){
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
int dijkstra(){
memset(dis,0x3f,sizeof dis);
dis[1]=0;
///建立一個維護最小值的優先隊列
priority_queue<PII,vector<PII>,greater<PII>>heap;
heap.push({0,1});///起始點放入隊列
while(heap.size()){
auto t=heap.top();///最小值
heap.pop();
int ver=t.second,d=t.first;
if(st[ver]) continue;///該點更新
st[ver]=true;
for(int i=h[ver];i!=-1;i=ne[i]){
int j=e[i];
if(dis[j]>d+w[i]){
dis[j]=d+w[i];
heap.push({dis[j],j});
}
}
}
if(dis[n]==0x3f3f3f3f) return -1;
return dis[n];
}
Bellman Ford
存在負權邊/限制邊數 O(n*m)
適合於m和n一個數量級
struct node{
int a,b,c;
}e[maxx];
int n,m,k;
int dis[maxn],last[maxn];///保證更新只是從上一次更新,不會被以前的影響
void BellmanFord(){
memset(dis,0x3f,sizeof dis);
dis[1]=0;
for(int i=0;i<k;i++){
memcpy(last,dis,sizeof dis);
for(int j=0;j<m;j++){
auto t=e[j];
dis[t.b]=min(dis[t.b],last[t.a]+t.c);
}
}
}
SPFA
存在負權邊 (相當於隊列優化的Bellman Ford)
int n, m;
int h[N], w[N], e[N], ne[N], idx;
int dist[N];
bool st[N];
void add(int a, int b, int c)
{
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}
int spfa()
{
memset(dist, 0x3f, sizeof dist);
dist[1] = 0;
queue<int> q;
q.push(1);
st[1] = true;
while (q.size())
{
int t = q.front();
q.pop();
st[t] = false;
for (int i = h[t]; i != -1; i = ne[i])
{
int j = e[i];
if (dist[j] > dist[t] + w[i])
{
dist[j] = dist[t] + w[i];
if (!st[j])
{
q.push(j);
st[j] = true;
}
}
}
}
return dist[n];
}
Floyd
Floyd 多源匯 O(n^3) 最短路
int n,m,q;
int d[maxn][maxn];
void init(){
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(i==j) d[i][j]=0;
else d[i][j]=inf;
}
void Floyd(){
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
}
4.最小生成樹算法
prim算法
int n, m;
int g[N][N];
int dist[N];
bool st[N];
int prim(){
memset(dist, 0x3f, sizeof dist);
int res = 0;
for (int i = 0; i < n; i ++ )
{
int t = -1;
for (int j = 1; j <= n; j ++ )
if (!st[j] && (t == -1 || dist[t] > dist[j]))
t = j;
if (i && dist[t] == INF) return INF;
if (i) res += dist[t];
st[t] = true;
for (int j = 1; j <= n; j ++ ) dist[j] = min(dist[j], g[t][j]);
}
return res;
}
kruskal
O(mlogm)
int n,m;
int root[maxn];
struct node{
int a,b,w;
}a[maxn];
bool cmp(node a,node b){
return a.w<b.w;
}
int Find(int x){
if(x!=root[x]) root[x]=Find(root[x]);
return root[x];
}
int kruskal(){
sort(a,a+m,cmp);
for(int i=1;i<=n;i++) root[i]=i;
int res=0,cnt=0;
for(int i=0;i<m;i++){
int aa=a[i].a;
int b=a[i].b;
int w=a[i].w;
aa=Find(aa),b=Find(b);
if(aa!=b){
root[aa]=b;
res+=w;
cnt++;
}
}
if(cnt<n-1) return INF;
else return res;
}
5.染色法判定二分圖
鏈式前向星存圖
#include<bits/stdc++.h>
using namespace std;
const int maxn=200010;
int n,m;
int h[100010],e[maxn],ne[maxn],idx;
int col[100010];
void add(int a,int b){
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
bool dfs(int u,int c){
col[u]=c;
for(int i=h[u];~i;i=ne[i]){
int j=e[i];
if(!col[j]){
if(!dfs(j,3-c)) return 0;
}
else if(col[j]==c) return 0;
}
return 1;
}
int main(){
cin>>n>>m;
memset(h,-1,sizeof h);
while(m--){
int x,y;
cin>>x>>y;
add(x,y);add(y,x);///無向圖
}
bool flag=1;
for(int i=1;i<=n;i++)
if(!col[i]){
if(!dfs(i,1)){
flag=0;
break;
}
}
if(flag) puts("Yes");
else puts("No");
return 0;
}
鄰接矩陣存圖
偷的學長的(超小聲)
vector<int>v[maxn];///vector存個圖
int vis[maxn];///vis標記顏色
ll n,m;
bool dfs(int u,int c)
{
vis[u]=c;
int sz=v[u].size();
for(int i=0;i<sz;i++)
{
int e=v[u][i];
if(vis[e]==c) return false;
if(!vis[e]&&!dfs(e,-c)) return false;
}
return true;
}
int main()
{
scanf("%lld%lld",&n,&m);
for(int i=1;i<=m;i++){
int x,y;scanf("%d%d",&x,&y);
v[x].push_back(y);
v[y].push_back(x);
}
for(int i=1;i<=n;i++)///圖可能不全是聯通的 判斷全部子圖均爲二分圖纔可以
if(vis[i]==0&&!dfs(i,1)){
printf("No");
return 0;
}
printf("Yes\n");
return 0;
}
模板來自AcWing
未完待續