学习之路(四) 最大生成树

这篇博客介绍了如何通过步骤来构建最大模糊支撑树。在三个家庭和一位客人的照片比较中,根据相貌相似度形成模糊关系矩阵,然后通过一系列操作找出最大权值的边,最终构成最大模糊支撑树。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

有三个家庭,每家的成员为4~7人,其中有一家有一个客人。取每人的照片一张放在一起,共16张,由和这三家人素不相识的中学生对照片两两进行比较,按相貌相似程度打分,得到16张照片的“ 相似” 模糊关系矩阵。

步骤:

(1)找出 G 中最大权值的边 rij;

(2)将 rij 存放在集合 C 中,将 rij 边上的新结点放入集合 T 中,若 T 中已含有所有 m 个结点时,转(4);

(3)检查 T 中每一个结点与 T 外的结点组成的边的权值,找出其中最大者 rij, 转至(2);

(4)结束,此时 G 中的边就构成了 G 的最大模糊支撑树 Tmax。

#include<stdio.h>
#include<stdlib.h>
const int M=16;
int pre[100]; //用于并查集
//存储最大生成树
int C1[100];
int C2[100];
double C3[100];
//存储砍掉部分枝后的边和权
int C4[100];
int C5[100];
double C6[100];
int C7[100]; //存储每个点的根结点
int C8[100]; //去除C7中重复的根结点,存储每类根结点
int T2[100]; //存储个数不为1的每一类的结点
int T3[100]; //存储所有个数不为1的类的结点

const double alpha=0.5;

typedef struct edge
{
	int begin[100]; //边点1
	int end[100]; //边点2
	double weight[100]; //权值
};

double findmax(double r[M][M]) //找出数组中的最大值
{
	double max=0;
	for(int i=1;i<M;i++)
	{
		for(int j=0;j<i;j++)
		{
			if(r[i][j]>max)
			{
				max=r[i][j];
			}
		}
	}
	return max;
}

int check(int T[],int node,int n_T) //判断数node是否是数组T中的结点
{
	for(int i=0;i<n_T;i++)
	{
		if(T[i]==node)
		{
			return 0;break;
		}
	}
	return 1;
}

int check2(edge C,int x,int y,int n_C) //判断x->y是否是C中的一条边
{
	for(int i=0;i<n_C;i++)
	{
		if(C.begin[i]==x&&C.end[i]==y)
		{
			return 0;break;
		}
	}
	return 1;
}

double find2(double r[M][M],int T[],edge C,int n_C) //找出数组的最大值且不为find中的值
{
	double max=0;
	for(int i=1;i<M;i++)
	{
		for(int j=0;j<i;j++)
		{
			if(r[i][j]>max)
			{
				if(check2(C,i,j,n_C)!=0)
				{
					max=r[i][j];
				}
			}
		}
	}
	return max;
}

//初始化根结点
void setPre(int n_T)
{
	for(int i=0;i<n_T;i++)
	{
		pre[i]=i; //初始化时,将自己初始化为自己的领导
	}
}

int find(int x)
{
	int r=x;
	while(pre[r]!=r)
	{
		r=pre[r]; //寻找根结点r
	}

	//路径压缩
	int i=x,j;
	while(i!=r)
	{
		j=pre[i]; //在改变上级之前用临时变量j记录它的值
		pre[i]=r; //把上级改为根结点
		i=j;
	}
	return r; //输出根结点
}

int exist(int data[],int v,int n)
{
	int i;
	for(i=0;i<n;i++)
	{
		if(v==data[i])
			return 0;
	}
	return 1;
}

int main()
{
	FILE *fp;
	if((fp=fopen("data.txt","r"))==NULL)
	{
		printf("Failed opening!\n");
		return 0;
	}
	else
	{
		printf("read ok!\n\n");
	}

	int i,j;
	double r[M][M]={{0.0}};

	//显示模糊矩阵
	printf("关系矩阵如下:\n");
	for(i=0;i<M;i++)
	{
		for(j=0;j<i+1;j++)
		{
			fscanf(fp,"%lf",&r[i][j]);
			printf("%.1lf ",r[i][j]);
		}
		printf("\n");
	}

	//寻找最大权值的边
	double max=findmax(r);

	edge C;
	int T[M];
	int n_C=0; //边数
	int n_T=0; //结点数
	//将rij存放于集合C中,新节点存放于集合T中
	for(i=1;i<M;i++)
	{
		for(j=0;j<i;j++)
		{
			if(r[i][j]==max)
			{
				if(n_T==0)
				{
					T[n_T]=i;n_T++;
					T[n_T]=j;n_T++;
					C.begin[n_C]=i;
					C.end[n_C]=j;
					C.weight[n_C]=r[i][j];
					n_C++;
				}

				if(check(T,i,n_T)!=0||check(T,j,n_T)!=0)
				{
					C.begin[n_C]=i;
					C.end[n_C]=j;
					C.weight[n_C]=r[i][j];
					n_C++;
				}

				if(check(T,i,n_T)!=0)
				{
					T[n_T]=i;
					n_T++;
				}
				if(check(T,j,n_T)!=0)
				{
					T[n_T]=j;
					n_T++;
				}
			}
		}
	}

	//检查T中节点与T外节点组成边的权值,找最大值rij转至步骤2
	printf("\n");
	double max2=0;
	while(n_T<16)
	{
		max2=find2(r,T,C,n_C);
		for(i=0;i<M;i++)
		{
			for(j=0;j<i;j++)
			{
				if(r[i][j]==max2)
				{
					C.begin[n_C]=i;
					C.end[n_C]=j;
					C.weight[n_C]=r[i][j];
					n_C++;

					if(check(T,i,n_T)!=0)
					{
						T[n_T]=i;
						n_T++;
					}
					if(check(T,j,n_T)!=0)
					{
						T[n_T]=j;
						n_T++;
					}
				}
			}
		}
	}

	printf("\n第一次生成:将所有结点存入T中,在不考虑成环情况下的所有边\n");
	printf("结点数:%d   边数:%d \n",n_C,n_T);
	printf("所有结点:");
	for(i=0;i<n_T;i++)
	{
		printf("%d ",T[i]+1);
	}

	printf("\n点1 点2 权值\n");
	for(i=0;i<n_C;i++)
	{
		printf("%-3d %-3d %-4.1lf\n",C.begin[i]+1,C.end[i]+1,C.weight[i]);
	}

	
	//使用并查集算法判断是否成环
	int n=0;
	setPre(n_T);
	for(i=0;i<n_C;i++)
	{
		int fx=find(C.begin[i]);
		int fy=find(C.end[i]);
		if(fx!=fy)
		{
			pre[fx]=fy;
			C1[n]=C.begin[i];
			C2[n]=C.end[i];
			C3[n]=C.weight[i];
			n++;
		}
	}

	printf("\n第二次生成:最大生成树\n");
	printf("结点数:%d   边数:%d \n",n,n_T);
	printf("点1 点2 权值\n");
	for(i=0;i<n;i++)
	{
		printf("%-3d %-3d %-4.1lf\n",C1[i]+1,C2[i]+1,C3[i]);
	}

	int n2;
	for(i=0;i<n;i++)
	{
		if(C3[i]<=alpha)
		{
			n2=i;break;
		}
		C4[i]=C1[i];
		C5[i]=C2[i];
		C6[i]=C3[i];
	}

	printf("\n第三次生成:砍掉权值不大于阈值的枝\n");
	printf("结点数:%d   边数:%d \n",n2,n_T);
	printf("点1 点2 权值\n");
	for(i=0;i<n2;i++)
	{
		printf("%-3d %-3d %-4.1lf\n",C4[i]+1,C5[i]+1,C6[i]);
	}

	//找出每个点的根结点
	setPre(n_T);
	for(i=0;i<n2;i++)
	{
		int fx=find(C4[i]);
		int fy=find(C5[i]);
		pre[fx]=fy;
	}

	for(i=0;i<n2;i++)
	{
		C7[2*i]=find(C4[i]);
		C7[2*i+1]=find(C5[i]);
	}

	printf("\n同类别结点\n");
	for(i=0;i<n2;i++)
	{
		printf("%-3d %-3d\n",find(C4[i])+1,find(C5[i])+1);
	}

	int n8=1;
	C8[0]=C7[0];
	for(i=1;i<2*n2;i++)
	{
		for(j=0;j<n8;j++)
		{
			if(C8[j]==C7[i])
			{
				break;
			}
			if(j==n8-1)
			{
				C8[n8]=C7[i];
				n8++;break;
			}
		}
	}

	int t=0;
	int z=0;
	while(t!=n8)
	{
		int n_t=0;
		for(i=0;i<n2;i++)
		{
			if(C8[t]==find(C4[i])&&exist(T2,C4[i],n_t))
			{
				T2[n_t]=C4[i];n_t++;
			}
			if(C8[t]==find(C5[i])&&exist(T2,C5[i],n_t))
			{
				T2[n_t]=C5[i];n_t++;
			}
		}
		t++;
		printf("\n第%d类:",t);
		for(i=0;i<n_t;i++)
		{
			T3[z]=T2[i];z++;
			printf("%d ",T2[i]+1);
		}
	}

	printf("\n");
	for(i=0;i<M;i++)
	{
		if(exist(T3,T[i],z))
		{
			t++;
			printf("第%d类:%d\n",t,T[i]+1);
		}
	}

	fclose(fp);
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值