并查集、实验六 图的应用

  • 树的并查算法
  • 实验六 图的应用  CCF-201709-4 通信网络

一、树的并查算法
使用并查集一般都有优化,合并和压缩,有的合并考虑树的高度,有的合并考虑根结点的权值等等。
在这个例子中,用编号代表每个元素,数组array表示的是父亲的编号,par[x]=x时,x是所在的树的根。
1、ParPtrTree.h

#include<iostream>
using namespace std;
#ifndef _ParPtrTree
#define _ParPtrTree
namespace wangzhe
{
	class ParPtrTree
	{
		private:
			int *array;
			int size;
		public:
			ParPtrTree(int sz);
			~ParPtrTree();
			int FIND(int curr) const;
                        void UNION(int a,int b);
                        bool same(int a,int b);		
	};
}
#endif 

2、ParPtrTree.cpp

#include<iostream>
using namespace std;
#include"ParPtrTree.h"
namespace wangzhe
{
	ParPtrTree::ParPtrTree(int sz)
	{
		size=sz;
		array=new int[sz];
		for(int i=0;i<sz;i++) 
			array[i]=i;
	}
	
	ParPtrTree::~ParPtrTree()
	{
		delete [] array;
	}
	
	/*//不考虑路径压缩 
	int ParPtrTree::FIND(int curr) const 
	{
		while(array[curr]!=ROOT) curr=array[curr];
		return curr;
	}*/
	
	//考虑路径压缩 
	int ParPtrTree::FIND(int curr) const 
	{
		if(array[curr]==curr) return curr;
		array[curr]=FIND(array[curr]);
		return array[curr];
	}
	
	void ParPtrTree::UNION(int a,int b)
	{
		int root1=FIND(a),root2=FIND(b);
		//合并方法多种多样,这里将根结点元素值大的指向小的 
		if(root1==root2) return;
		else if(root1<root2) array[root2]=root1;
		else array[root1]=root2;
	}
	
	bool ParPtrTree::same(int a, int b) 
	{
		int root1=FIND(a),root2=FIND(b);                  
		return root1==root2;         
	}
}

3、main.cpp

#include <iostream>
using namespace std; 
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
#include"ParPtrTree.h"
using namespace wangzhe;
int main(int argc, char** argv) 
{
	ParPtrTree *ppt=new ParPtrTree(100);
	cout<<ppt->same(3,1)<<endl;
	cout<<ppt->FIND(3)<<endl;
	cout<<ppt->FIND(1)<<endl;
	cout<<endl;
	ppt->UNION(3,1);
	cout<<ppt->same(3,1)<<endl;
	cout<<ppt->FIND(3)<<endl;
	cout<<ppt->FIND(1)<<endl;
	return 0;
}

4、运行结果

 

二、实验六 图的应用  CCF-201709-4 通信网络

删除(注释)了部分无用的代码,添加了一些数据成员和方法,此为提交判分的版本,一开始拿了5分,修修改改甚至拿了0分(崩溃)

想了N载春秋,最后在什造大佬的提点下,醍醐灌顶,原来是SetEdge方法添加边时若两个顶点相同的话,原先的代码会给出提示信息:“Illegal Vertex”。遂将这句提示信息给注释掉再提交,60分,剩下四个样例超时了,应该用邻接表的,但已经满足老师规定的要求了遂不再费时修改。

看来这数据还是有点意思。
满分可以参考以前写的这篇CCF-CSP-201709-4 通信网络

60分超时代码:

//CSDN博客:https://blog.csdn.net/qq_40889820
#include<iostream>
#include<sstream>
#include<algorithm>
#include<string>
#include<cstring>
#include<iomanip>
#include<vector>
#include<cmath>
#include<ctime>
#include<stack>
#include<queue>
#include<map>
#define mem(a,b) memset(a,b,sizeof(a))
//#define e 2.71828182
#define VISITED 1//标记状态,设为1 
#define UNVISITED 0//未标记状态,设为0 
#define INF 1<<30//无穷大,设为1<<30 
#define Pi 3.141592654
using namespace std;//标准命名空间 
class Graph
{
        private:
		void operator = (const Graph&) {}
		Graph(const Graph&) {}
	public:
		Graph() {}//构造函数 
		virtual ~Graph() {}//析构函数 
		virtual int n() =0;//顶点数目 
		//virtual int e() =0;//边数目 
		virtual int first(int v) =0;//与顶点v关联的第一条边 
		virtual int next(int v,int w) =0;//与顶点v关联的在顶点w后的下一条边 
		virtual void clear()=0;//销毁一个图 
		virtual bool setEdge(int v1,int v2,int wght) =0;//设边权 
		//virtual bool delEdge(int v1,int v2) =0;//删除边 
		//virtual bool isEdge(int i,int j) =0;//判断是否有边 
		//virtual int weight(int v1,int v2) =0;//v1-v2边的边权 
		virtual int getMark(int v) =0;//返回标记信号 
		virtual void setMark(int v,int val) =0;//标记数组中做标记 
		//virtual void DFS(Graph *G,int v) =0;//深搜 
};

class Graphm:public Graph
{
	private:
		int numVertex,numEdge;//顶点数、边数 
		int **matrix;//邻接矩阵 
		int *mark;//标记数组
		int **iskno; 
	public:
		Graphm();//无参构造函数 
		~Graphm();//析构函数 
		void Init(int n);//初始化 
		int n();//返回顶点数 
		//int e();//返回边数 
		int first(int v);//与顶点v第一个相关联的边 
		int next(int v,int w);//与顶点v在顶点w后相关联的边 
		void clear();//清空 
		bool setEdge(int v1,int v2,int wt);//增添边或修改边 
		//bool delEdge(int v1,int v2);//删除边 
		//bool isEdge(int i,int j);//判断是否是边 
		//int weight(int v1,int v2);//返回权值 
		int getMark(int v);//返回标记状态 
		void setMark(int v,int val);//标记 
                void memset();//重置mark数组 
		//void DFS(Graph *G,int v);//深度优先搜索 
		//void print();//输出邻接矩阵
		void fun(int v1,int v2);//求出第v1个部门知道的部门
		int solve();//求解 
};  
  
Graphm::Graphm()
{
		
}
	
Graphm::~Graphm()
{
	clear(); 
}
	
void Graphm::Init(int n)
{
	numVertex=n;
	numEdge=0;
	mark=new int[n];
	for(int i=0;i<numVertex;i++)
		mark[i]=UNVISITED;
	matrix=(int**) new int*[numVertex];
	for(int i=0;i<numVertex;i++)
	   	matrix[i]=new int[numVertex];
	for(int i=0;i<numVertex;i++)
	   	for(int j=0;j<numVertex;j++)
	   	{
	    	if(i==j) matrix[i][j]=0;
	    	else matrix[i][j]=INF;
		}
		
			iskno=(int**) new int*[numVertex];
		for(int i=0;i<numVertex;i++)
	    	iskno[i]=new int[numVertex];
	        for(int i=0;i<numVertex;i++)
	    	        for(int j=0;j<numVertex;j++)
	    		iskno[i][j]=0;
}
	
int Graphm::n()
{
	return numVertex;
}
	
int Graphm::first(int v)
{
	for(int i=0;i<numVertex;i++)
		if(matrix[v][i]!=INF&&matrix[v][i]!=0) return i;
	return numVertex;
}
	
int Graphm::next(int v,int w)
{
	for(int i=w+1;i<numVertex;i++)
		if(matrix[v][i]!=INF&&matrix[v][i]!=0) return i;
	return numVertex;
}
	
void Graphm::clear()
{
	delete [] mark;
	mark=NULL;//释放后置空 
	for(int i=0;i<numVertex;i++) 
		delete [] matrix[i];
	delete [] matrix;
	matrix=NULL;//释放后置空 
}
	
bool Graphm::setEdge(int v1,int v2,int wt)
{
	if(wt<=0)
	{
		cout<<"Illegal weight value\n";
		return false;
	}
	/*if(v1==v2||v1<0||v2<0||v1>=numVertex||v2>=numVertex)
	{
		cout<<"Illegal vertex\n";
		return false;
	}*/
	if(matrix[v1][v2]==INF) numEdge++;
	matrix[v1][v2]=wt;
	return true;
}
			
int Graphm::getMark(int v)
{
	return mark[v];
}
	
void Graphm::setMark(int v,int val)
{
	mark[v]=val;
}
	
void Graphm::memset()
{
	for(int i=0;i<numVertex;i++)
		mark[i]=UNVISITED;	
}
	
void Graphm::fun(int v1,int v2)
{
	setMark(v1,VISITED);//标记
	//直接或间接传递信息时,他们才彼此知道对方的存在
	iskno[v1][v2]=1;
	iskno[v2][v1]=1;
	for(int w=first(v1);w<n();w=next(v1,w))
		if(getMark(w)==UNVISITED) 
			fun(w,v2);	
}
	
int Graphm::solve()
{
	int j,ans=0;
	for(int i=0;i<numVertex;i++)
	{
		for(j=0;j<numVertex&&iskno[i][j];j++);
		if(j==numVertex) ans++; //知道所有其他部门的存在 
	}
	return ans;//ans个部门所知道的部门数量(包括自己)正好是N
}

int main()
{
	Graphm G;//图 
	int N,M;//部门的数量、单向通道的数量 
	cin>>N>>M;//输入 
	G.Init(N);//N个部门 
	for(int i=0;i<M;i++)//M条单向通道 
	{
		int a,b;//部门a到部门b有一条单向通路
		cin>>a>>b;//输入
		a--;b--;//图的实现里顶点数是0到n-1,而题目中是1到n,所以需要转换一下 
		G.setEdge(a,b,1);//邻接矩阵matrix[a][b]置为1 
	}
	
	for(int i=0;i<N;i++)//求解第i个部门对其他部门的了解性 
        {
    	        G.fun(i,i);//调用fun方法 
    	        G.memset();//每次求解后都要对标记数组重置
	}
	cout<<G.solve();//输出结果 
	return 0;//程序结束,返回0 
	
}

 

展开阅读全文

没有更多推荐了,返回首页