該 blog 持續更新。
1.虛點
虛點,是指在圖中建立一個不存在的源點,把這個源點和其他的一些點連線起來(可以全連,也可以只連一部分;邊權可以為 0 ,也可以不為 0 。),通常作用為:
- 把所有點加入同一個連通圖中,如 SPFA 判斷負環建立一個邊權為 0 的虛點 (SPFA判負環)。
- 處理自環的邊權,如下文中例題:買票(最小生成樹建虛點例題)。
(1) SPFA判負環
題面
題解
初始化 SPFA 時將所有節點加入 queue 中,就相當於建立了一個虛擬源點,與原圖中的所有節點連了一條邊權為 0 的從虛點指向該點的有向邊,然後判斷負環。
點選檢視程式碼
#include <bits/stdc++.h>
using namespace std;
int n,m,x,y,z;
int h[2005],e[10005],ne[10005],w[10005],idx=0;
int dis[2005],cnt[2005];
queue<int> q;
bool inq[2005];
void add(int a,int b,int c)
{
e[++idx]=b;
w[idx]=c;
ne[idx]=h[a];
h[a]=idx;
}
bool spfa()
{
for(int i=1;i<=n;i++)//虛擬源點的思想,邊權為0
{
q.push(i);
inq[i]=1;
}
while(!q.empty())
{
int u=q.front();
q.pop();
inq[u]=0;
for(int i=h[u];i!=-1;i=ne[i])
{
int v=e[i],nw=w[i];
if(dis[u]+nw<dis[v])
{
dis[v]=dis[u]+nw;
cnt[v]=cnt[u]+1;
if(cnt[v]>=n)
{
return 1;
}
if(inq[v]==0)
{
q.push(v);
inq[v]=1;
}
}
}
}
return 0;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
memset(h,-1,sizeof(h));
for(int i=1;i<=m;i++)
{
cin>>x>>y>>z;
add(x,y,z);
}
if(spfa())cout<<"Yes";
else cout<<"No";
return 0;
}
(2) 最小生成樹建虛點例題
題面
題解
給自己買票相當於給虛擬源點連一條邊權為 \(w\) 的邊,因為至少要有一個人給自己買票才能給其他人買票,所以就此跑最小生成樹即可。本題程式碼用編號為 0 的點表示虛擬源點。
PS: 本題因為邊數較多,實際上用 Prim 演算法的時間更優,但下面程式碼用了 Kruskal 演算法。
點選檢視程式碼
#include <bits/stdc++.h>
using namespace std;
int n,m,x,y,w,ans=0;
struct edge{
int u,v,w;
};
edge e[4000005];
bool cmp(edge a,edge b)
{
return a.w<b.w;
}
int dsu[2005];
int findf(int a)
{
if(dsu[a]!=a)dsu[a]=findf(dsu[a]);
return dsu[a];
}
void combine(int a,int b)
{
int fa=findf(a),fb=findf(b);
dsu[fa]=fb;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=2000;i++)dsu[i]=i;
for(int i=1;i<=m;i++)
{
cin>>x>>y>>w;
if(x==y)
{
e[i]={0,x,w};
}
else
{
e[i]={x,y,w};
}
}
sort(e+1,e+m+1,cmp);
for(int i=1;i<=m;i++)
{
if(findf(e[i].u)!=findf(e[i].v))
{
combine(e[i].u,e[i].v);
ans+=e[i].w;
}
}
cout<<ans;
return 0;
}
2.反圖
to be continued...