图论合集

最小生成树

kruskal

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=2e5+10;
ll n,m,f[N],l,cnt,ans;
struct line{
	ll x,y,l;
}li[N];
bool cmp(line x,line y){
	return x.l<y.l;
}
void init(){
	for(ll i=1;i<=n;i++){
		f[i]=i;
	}
}
ll find(ll x){
	if(f[x]==x) return x;
	return f[x]=find(f[x]);
}
int main(){
	cin >> n >> m;
	for(ll i=1;i<=m;i++){
	scanf("%lld%lld%lld",&li[i].x,&li[i].y,&li[i].l);
	}
	sort(li+1,li+m+1,cmp);
	init();
	for(ll i=1;i<=m;i++){
		if(find(li[i].x)==find(li[i].y)) continue;
		cnt++;
		ans+=li[i].l;
		f[find(li[i].x)]=find(li[i].y);
		if(cnt==n) break;
	}
	cout << ans;
	return 0;
}

最短路

dijkstra

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=5e5+5;
const ll INF=1e18+7;
ll n,m,s;
vector<ll> e[N],u[N];
ll d[N],t[N];
priority_queue<pair<ll,ll>,vector<pair<ll,ll> >,greater<pair<ll,ll> > >pq;
void dijkstra(){
	fill(d+1,d+n+1,INF);
	d[s]=0;
	pq.push(make_pair(0, s));
	while(pq.size()){
		ll h1=pq.top().first;
		ll h2=pq.top().second;
		pq.pop();
		if(t[h2]) continue;
		t[h2]=1;
		for(ll i=0;i<e[h2].size();i++){
			if(h1+u[h2][i]<d[e[h2][i]]){
				d[e[h2][i]]=h1+u[h2][i];
				pq.push(make_pair(h1+u[h2][i],e[h2][i]));
			}
		}
	}
}
int main(){
	scanf("%lld%lld",&n,&m);
	s=1;
	for(ll i=1;i<=m;i++){
		ll x,y,z;
		scanf("%lld%lld%lld",&x,&y,&z);
		e[x].push_back(y);
		u[x].push_back(z);
	}
	dijkstra();
	for(ll i=1;i<=n;i++){
		if(d[i]==INF) printf("inf ");
		else printf("%lld ",d[i]);
	}
	return 0;
}

SPFA

#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10;
const int INF=1e9+7;
int n,m,s,d[N],l[N],in[N];
int fi[N],ne[N],to[N],ew[N],cnt;
queue<int> q;
bool spfa(){
	fill(d,d+N,INF);
	d[s]=0;
	q.push(s);
	while(q.size()){
		int u=q.front();
		q.pop();
		in[u]=0;
		for(int i=fi[u];i;i=ne[i]){
			if(d[u]+ew[i]<d[to[i]]){
				l[to[i]]=l[u]+1;
				if(l[to[i]]>n){
					return 0;
				}
				d[to[i]]=d[u]+ew[i];
				if(!in[to[i]]) q.push(to[i]);
			}
		}
	}
	return 1;
}
void add(int x,int y,int z){
	to[++cnt]=y;
	ew[cnt]=z;
	ne[cnt]=fi[x];
	fi[x]=cnt;
}
int main(){
	scanf("%d%d%d",&n,&m,&s);
	int x,y,z;
	for (int i=1;i<=m;i++) {
		scanf("%d%d%d",&x,&y,&z);
		add(x,y,z);
	}
	if (spfa()) {
	    for (int i=1;i<n;i++) printf("%d ",d[i]);
	    printf("%d\n",d[n]);
    }
    else {
        printf("-1\n");
    }
	return 0;
}

缩点

#include<bits/stdc++.h>
using namespace std;

const int maxn=10000+15;
int n,m,sum,tim,top,s;
int p[maxn],head[maxn],sd[maxn],dfn[maxn],low[maxn];
int stac[maxn],instack[maxn]; 
int h[maxn],in[maxn],dist[maxn];
struct EDGE
{
	int to;int next;int from;
}edge[maxn*10],ed[maxn*10];
void add(int x,int y)
{
	edge[++sum].next=head[x];
	edge[sum].from=x;
	edge[sum].to=y;
	head[x]=sum;
}
void tarjan(int x)
{
	low[x]=dfn[x]=++tim;
	stac[++top]=x;instack[x]=1;
	for (int i=head[x];i;i=edge[i].next)
	{
		int v=edge[i].to;
		if (!dfn[v])
        {
		    tarjan(v);
		    low[x]=min(low[x],low[v]);
	    }
	    else if (instack[v])
	    {
	    	low[x]=min(low[x],dfn[v]);
		}
	}
	if (dfn[x]==low[x])
	{
		int y;
		while (y=stac[top--])
		{
			sd[y]=x;
			instack[y]=0;
			if (x==y) break;
			p[x]+=p[y];
		}
	}
}
int topo()
{
	queue <int> q;
	int tot=0;
	for (int i=1;i<=n;i++)
	    if (sd[i]==i&&!in[i])
	    {
		    q.push(i);
            dist[i]=p[i];
	    }
	while (!q.empty())
	{
		int k=q.front();q.pop();
		for (int i=h[k];i;i=ed[i].next)
		{
			int v=ed[i].to;
			dist[v]=max(dist[v],dist[k]+p[v]);
			in[v]--;
			if (in[v]==0) q.push(v);
		}
	}
    int ans=0;
    for (int i=1;i<=n;i++)
        ans=max(ans,dist[i]);
    return ans;
}
int main()
{
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++)
	    scanf("%d",&p[i]);
	for (int i=1;i<=m;i++)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		add(u,v);
	}
	for (int i=1;i<=n;i++)
	    if (!dfn[i]) tarjan(i);
	for (int i=1;i<=m;i++)
	{
		int x=sd[edge[i].from],y=sd[edge[i].to];
		if (x!=y)
		{
			ed[++s].next=h[x];
			ed[s].to=y;
			ed[s].from=x;
			h[x]=s;
			in[y]++;
		}
	}
	printf("%d",topo());
	return 0;
}

例题

[最小生成树]USACO08OCT Watering Hole G

链接:https://www.luogu.com.cn/problem/P1550
建造水源虚拟点

#include <bits/stdc++.h>
using namespace std;
const int N=500;
int n,a[N],e[N][N],f[N],l,cnt,ans;
struct line{
	int x,y,l;
}li[N*N];
bool cmp(line x,line y){
	return x.l<y.l;
}
void init(){
	for(int i=1;i<=n+1;i++){
		f[i]=i;
	}
}
int find(int x){
	if(f[x]==x) return x;
	return f[x]=find(f[x]);
}
int main(){
    cin >> n;
    for(int i=1;i<=n;i++) cin >> a[i];
    for(int i=1;i<=n;i++){
    	for(int j=1;j<=n;j++){
    		cin >> e[i][j];
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=i+1;j<=n;j++){
			li[++l]={i, j, e[i][j]};
		}
	}
	for(int i=1;i<=n;i++){
		li[++l]={i,n+1, a[i]};
	}
	sort(li+1,li+l+1,cmp);
	init();
	for(int i=1;i<=l;i++){
		if(find(li[i].x)==find(li[i].y)) continue;
		cnt++;
		ans+=li[i].l;
		f[find(li[i].x)]=find(li[i].y);
		if(cnt==n) break;
	}
	cout << ans;
	return 0;
}

[缩点] USACO03FALL / HAOI2006 受欢迎的牛 G

链接:https://www.luogu.com.cn/problem/P2341

#include <bits/stdc++.h>
using namespace std;
const int N=5e4+4;
int n,m,cnt;
stack<int>s;
int fi[N],ne[N],to[N];
int dfn[N],low[N];
int du[N],id[N],all[N];
int ins[N],tnt,g;
void add(int x,int y){
	to[++cnt]=y;
	ne[cnt]=fi[x];
	fi[x]=cnt;
}
void tarjan(int x){
	dfn[x]=low[x]=++tnt;
	s.push(x);
	ins[x]=1;
	for(int i=fi[x];i;i=ne[i]){
		int u=to[i];
		if(!dfn[u]){
			tarjan(u);
			low[x]=min(low[x],low[u]);
		}
		else if(ins[u]){
			low[x]=min(low[x],dfn[u]);
		}
	}
	int k;
	if(low[x]==dfn[x]){
		g++;
		do{
			k=s.top();
			s.pop();
			ins[k]=0;
			id[k]=g;
			all[g]++;
		}while(x!=k);
	}
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		int x,y;
		scanf("%d%d",&x,&y);
		add(x, y);
	}
	for(int i=1;i<=n;i++){
		if(!dfn[i]) tarjan(i);
	}
	for(int i=1;i<=n;i++){
		for(int j=fi[i];j;j=ne[j]){
			int u=to[j];
			if(id[i]!=id[u]){
				du[id[i]]++;
			}
		}
	}
	int t=0;
	for(int i=1;i<=g;i++){
		if(!du[i]){
			if(t) {
				printf("0\n");
				return 0;
			}
			t=i;
		}
	}
	printf("%d\n",all[t]);
	return 0;
}

[基环树] NOIP2018 提高组 旅行

#include <bits/stdc++.h>
using namespace std;
const int N=10005;
int fi[N],ne[N],to[N],fo[N],l,cnt,v[N],dep;
vector<int> e[N];
int ans[N],k[N];
int n,m,l1,l2;
int read()//快读模板 
{
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
void add(int x,int y){ //链式前向星 
	ne[++l]=fi[x];
	to[l]=y;
	fo[l]=x;
	fi[x]=l;
}
void dfs1(int x){
	cnt++;
	ans[cnt]=x;
	v[x]=1;
	for(int i=0;i<e[x].size();i++){
		if(v[e[x][i]]==0){
			dfs1(e[x][i]);
		}
	}
}
void dfs2(int x,int f){
	if(v[x]) return;
	v[x]=1;
	k[++dep]=x;//打擂台答案 
	for(int i=0;i<e[x].size();i++){
		int r=e[x][i];
		if(r==f) continue;
		if(x==l1&&e[x][i]==l2||x==l2&&e[x][i]==l1){
			continue; // 重复边 
		}
		dfs2(e[x][i],x);
	}
}

bool check(){
	for(int i=1;i<=n;i++){
		if(k[i]==ans[i]) continue;
		if(k[i]>ans[i]) return 0;
		return 1;
	}
}
void change(){ // 将打擂台答案复制 
	for(int i=1;i<=n;i++) ans[i]=k[i];
}
int main(){
	n=read();m=read();
	for(int i=1;i<=m;i++){
		int x,y;
		x=read();y=read();
		// 链式前向星与vector存储 
		e[x].push_back(y);
		e[y].push_back(x);
		add(x, y);
		add(y, x);
	}
	for(int i=1;i<=n;i++){//由于字典序,需要将所有边排序 
		sort(e[i].begin(),e[i].end());
	}
	if(n==m+1){//普通的树 
		dfs1(1);
		for(int i=1;i<=n;i++){
			printf("%d ",ans[i]);
		}
	}
	else{//基环树 
		for(int i=0;i<l;i+=2){//基环树不符合贪心,需要一个一个边枚举 
			dep=0;
			memset(v, 0, sizeof(v));
			l1=fo[i];l2=to[i];
			dfs2(1,-1);
			if(dep<n){
				continue;
				//没有遍历所有节点 
			}
			if(ans[1]==0) change(); //没答案,将打擂台的答案复制 
			else if(check()) change(); //打擂台答案比原答案字典序小 
		}
		for(int i=1;i<=n;i++){
			printf("%d ",ans[i]);
		}
	}
	return 0;
}
posted @ 2022-07-13 14:33  KevinLikesCoding  阅读(35)  评论(0编辑  收藏  举报