POJ 1679 The Unique 次最小生成树 MST

http://poj.org/problem?id=1679

题目大意:

给你一些点,判断MST(最小生成树)是否唯一。

思路:

以前做过这题,不过写的是O(n^3)的,今天学了一招O(n^2)的,哈哈~

方法一:

首先先建立MST,然后把这个MST的边一个个尝试不使用,构建另外一颗MST,然后判断权值是否相等。

这样复杂度需要O(n^3)。。

方法二:

还可以用次最小生成树的方法解决:如果最小生成树不唯一,那么次小生成树的权值和最小生成树相同。

我们可以枚举要加入哪一条新边。在最小生成树上加一条边u-v之后,图上会出现一条回路。因此删除的边必须在最小生成树u到v的路径上,而且是这条路径上的最长边。而次最小生成树一定能由最小生成树加一条再删除一条边得到。只需我们求出没对结点u和v在最小生成树中唯一路径的最大边权dp[i][j],剩下的只需O(m)时间,枚举所有的m-n+1条边进行交换,每次花O(1)时间求出新生成树的权值。总时间复杂度为O(n^2)

方法一:

#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN=101;
int fa[MAXN];

struct point
{
	int x,y;
	int len;
}data[MAXN*MAXN],pre[MAXN*MAXN]; //pre 记录等一下用到了哪些条边

bool operator < (const point &a ,const point &b) //sort 重载比较函数
{
	return a.len<b.len;
}

void UFinit(int n)     //并查集初始化
{
	for(int i=1;i<=n;i++)
		fa[i]=i;
}

int find(int cur)      //带路径压缩的并查集查询函数,简洁而优雅~
{
	return fa[cur]==cur? cur : fa[cur]=find(fa[cur]);
}

int kruskal(int n,int m,int &prelen)
{
	UFinit(n);
	int ans=0;
	for(int i=0;i<m;i++)
	{
		int rootx=find(data[i].x);
		int rooty=find(data[i].y);
		if(rootx!=rooty)
		{
			fa[rootx]=rooty;
			ans+=data[i].len;
			pre[prelen++]=data[i];

		}
	}

	return ans;
}

int kruskal2(int n,int m,int nox,int noy)//nox noy 代表直接连接这两个点之间的边不选
{
	UFinit(n);

	int ans=0;
	for(int i=0;i<m;i++)
	{
		if(data[i].x==nox && data[i].y==noy)
			continue;

		int rootx=find(data[i].x);
		int rooty=find(data[i].y);
		if(rootx!=rooty)
		{
			fa[rootx]=rooty;
			ans+=data[i].len;	
		}
	}

	return ans;

}
int main()
{

	int T;
	scanf("%d",&T);
	while(T--)
	{
		int n,m;
		scanf("%d%d",&n,&m);
		for(int i=0;i<m;i++)
			scanf("%d%d%d",&data[i].x,&data[i].y,&data[i].len);

		int prelen=0;                      //pre数组的长度
		sort(data,data+m);					//kruskal 前面的工作

		int ans=kruskal(n,m,prelen);        //正常版本的kruskal

		bool unique=true;           

		for(int i=0;i<prelen;i++)
		{			
			int res=kruskal2(n,m,pre[i].x,pre[i].y);//带去除边的kruskal
			
			int cnt=0;
			for(int j=1;j<=n;j++)
				if(fa[i]==i)
					cnt++;               

			if(cnt!=0)    //这个很重要!没有就WA,因为可能剩下的不连通,但是恰好res==ans
				continue;

			if(res==ans)   //如果还有一棵生成树和第一次所求的权值一样,说明最小生成树不唯一
			{
				unique=false;
				break;
			}
		}

		if(unique)
			printf("%d\n",ans);
		else
			printf("Not Unique!\n");
	}
	return 0;
}


方法二:
#include<cstdio>  
#include<cstring>  
#include<algorithm>  
using namespace std;
const int MAXN=100+10;  
int n,m,head[MAXN],len,fa[MAXN],nodes[MAXN],n_len;
int dp[MAXN][MAXN];
bool vis[MAXN];
int find(int cur)      //带路径压缩的并查集查询函数,简洁而优雅~  
{  
    return fa[cur]==cur? cur : fa[cur]=find(fa[cur]);  
}  

struct edge      //MST邻接表  
{  
    int to,next;  
    double val; 
}e[MAXN*MAXN];  
  
void add(int from,int to,double val)  
{  
    e[len].to=to;  
    e[len].val=val;  
    e[len].next=head[from];  
    head[from]=len++;  
}  
struct point  
{  
    int x,y;  
    int len;  
	bool operator < (const point &b) const//sort 重载比较函数  
	{  
	  return len<b.len;  
	}  
  
}data[MAXN*MAXN];
 

void dfs(int cur,int fa,int dis)
{
	for(int i=0;i<n_len;i++)
	{
		int x=nodes[i];
		dp[cur][x]=dp[x][cur]=max(dp[x][fa],dis);
	}
	

	nodes[n_len++]=cur;

	for(int i=head[cur];i!=-1;i=e[i].next)
	{
		int id=e[i].to;
		if(id != fa)   
			dfs(id,cur,e[i].val);
	}
}

int main()
{
	int T;
	scanf("%d",&T);
	while(T--)
	{
		memset(head,-1,sizeof(head));
		len=0;
		memset(vis,0,sizeof(vis));
		scanf("%d%d",&n,&m);
		for(int i=0;i<m;i++)  
            scanf("%d%d%d",&data[i].x,&data[i].y,&data[i].len);  

		for(int i=1;i<=n;i++)
			fa[i]=i;

		//kruskal
		sort(data,data+m);
		int mstlen=0;
		for(int i=0;i<m;i++)
		{
			int x=data[i].x,y=data[i].y;
			int root_x=find(x),root_y=find(y);
			if(root_x!=root_y)
			{
				mstlen+=data[i].len;
				fa[root_x]=root_y;
				add(x,y,data[i].len);     //kruskal中顺便建立MST的邻接表
				add(y,x,data[i].len);
				vis[i]=true;
			}
		}
		n_len=0;
		memset(dp,0,sizeof(dp));
		dfs(1,-1,0);
		int mstlen2=9999999;
		for(int i=0;i<m;i++)
		{
			if(vis[i]) continue;
			int x=data[i].x,y=data[i].y,dis=data[i].len;
			mstlen2=min(mstlen2,mstlen - dp[x][y] + dis);
		}
	//	printf("%d\n",mstlen2);
		if(mstlen == mstlen2)
			puts("Not Unique!");
		else
			printf("%d\n",mstlen);
	}
	return 0;
}




POJ 1679 The Unique 次最小生成树 MST

上一篇:Windows终端安装git


下一篇:【深山老林Spring】加载Bean定义信息的两员大将:AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner