模拟赛 最小环 题解

求包含1号点的最小环。

这个最小环一定是从1点的出边指向的点出发,再回到另一个1点的出边指向的点。

这等价于1号点所有出边指向的点中,两两之间最短路+1号点到这两个点的距离的最小值。

使用二进制拆分,分成两组点,分别向s,t连边,正反算两次最短路。

时间复杂度 \(O((n+m)log^2 n)\)

二进制分组是很常用的思路。

参考代码:

#include <stdio.h>
#include <queue>
using namespace std;
int fr[5010],ne[30010],inf=999999999;
int v[30010],w[30010],bs=0,n;
int jl[5010],sz[5010],cd1[5010],cd2[5010];
bool bk[5010];
void addb(int a,int b,int c)
{
	v[bs]=b;
	w[bs]=c;
	ne[bs]=fr[a];
	fr[a]=bs;
	bs+=1;
}
struct SJd               
{                        
    int u,z;
	SJd(){};
	SJd(int Z,int U)
	{
		z=Z;
		u=U;
	}
	bool operator<(const SJd&a)const
	{
		return z>a.z;
	}
};
int dij(int S,int T)
{
	for(int i=1;i<=n;i++)
	{
		bk[i]=false;
		jl[i]=inf;
	}
	jl[S]=0;
	priority_queue<SJd> dl;
	dl.push(SJd(0,S));
    while(!(dl.empty()))
    {
		SJd	t=dl.top();
		dl.pop();
		if(bk[t.u])
			continue;
		bk[t.u]=true;
        for(int j=fr[t.u];j!=-1;j=ne[j])                  
        {      
			if(v[j]==1)
				continue;
            if(t.z+w[j]<jl[v[j]])                        
            { 
				jl[v[j]]=t.z+w[j];
                dl.push(SJd(jl[v[j]],v[j]));
            }
        }                                                
    }
	return jl[T];
}
int main()
{
	freopen("B.in","r",stdin);
	freopen("B.out","w",stdout);
	int m,k=0,jg=inf;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n+2;i++)
		fr[i]=-1;
	for(int i=0;i<m;i++)
	{
		int a,b,c,d;
		scanf("%d%d%d%d",&a,&b,&c,&d);
		addb(a,b,c);
		addb(b,a,d);
	}
	for(int i=fr[1];i!=-1;i=ne[i])
	{
		sz[k]=v[i];
		cd1[k]=w[i];
		cd2[k]=w[i^1];
		k+=1;
	}
	n+=2;
	for(int i=16;i>=0;i--)
	{
		for(int j=0;j<k;j++)
		{
			if(j&(1<<i))
			{
				addb(n,sz[j],cd1[j]);
				addb(sz[j],n,cd1[j]);
			}
			else
			{
				addb(n-1,sz[j],cd2[j]);
				addb(sz[j],n-1,cd2[j]);
			}
		}
		int rt=dij(n,n-1);
		if(rt<jg)
			jg=rt;
		bs-=k*2;
		for(int j=0;j<k;j++)
			fr[sz[j]]=ne[fr[sz[j]]];
		fr[n]=fr[n-1]=-1;
		//
		for(int j=0;j<k;j++)
		{
			if(!(j&(1<<i)))
			{
				addb(n,sz[j],cd1[j]);
				addb(sz[j],n,cd1[j]);
			}
			else
			{
				addb(n-1,sz[j],cd2[j]);
				addb(sz[j],n-1,cd2[j]);
			}
		}
		rt=dij(n,n-1);
		if(rt<jg)
			jg=rt;
		bs-=k*2;
		for(int j=0;j<k;j++)
			fr[sz[j]]=ne[fr[sz[j]]];
		fr[n]=fr[n-1]=-1;
	}
	printf("%d",jg);
	fclose(stdin);
	fclose(stdout);
	return 0;
}
posted @ 2019-07-25 20:35  lnzwz  阅读(175)  评论(0编辑  收藏  举报