圖論雜項小技巧

KS_Fszha發表於2024-03-30

該 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...

相關文章