- 树的并查算法
- 实验六 图的应用 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
}