1034 Head of a Gang (30分)图基本算法 图的表示方法 邻接矩阵 邻接表

https://www.cnblogs.com/dzkang2011/p/graph_1.html

https://blog.csdn.net/lu_LLLR/article/details/82453722?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param

//A "Gang" is a cluster of more than 2 persons 
//who are related to each other with total relation weight being greater than a given threshold K

// The weight of a relation 
// is defined to be the total time length of all the phone calls 
// made between the two persons

// For each case, 
// the first line contains two positive numbers N and K (both less than or equal to 1000), 

// the number of phone calls and the weight threthold, respectively. 
// 8 59
//Then N lines follow, each in the following format:
//Name1 Name2 Time
// AAA BBB 10
// BBB AAA 20
// AAA CCC 40
// DDD EEE 5
// EEE DDD 70
// FFF GGG 30
// GGG HHH 20
// HHH FFF 10

//first print in a line the total number of gangs. 
//print in a line the name of the head and the total number of the members. 
//It is guaranteed that the head is unique for each gang. 

//In each gang, the one with maximum total weight is the head.
// 2
// AAA 3
// GGG 3

#include <iostream>
#include <map>
using namespace std;
const int maxn=10100;

int n,k,numPerson=0;//边数 下限k 总人数numPerson
int G[maxn][maxn]={
    
    0},weight[maxn]={
    
    0};
bool vis[maxn]={
    
    false};//标记是否被访问,初始化赋值为false,未访问

map<int,string>intToString;//编号->姓名
map<string,int>stringToInt;//姓名->编号
map<string,int>Gang;//head->人数



///图的遍历,dfs你一定要传入id编号参数吧,深度啥的不一定

//返回姓名str对应的编号
//搞了半天,这是在对map进行操作啊!!!!!!!!!!map<string,int>map<int,string>
int change(string str){
    
    
    if(stringToInt.find(str)!=stringToInt.end()){
    
    //如果str已经出现过///map<string,int>stringToInt;//姓名->编号--AAA
        return stringToInt[str];//-----1
    }else{
    
    
        stringToInt[str]=numPerson;//str的编号是numPerson/map<string,int>stringToInt;//姓名->编号---AAA-1
        intToString[numPerson]=str;//map<int,string>intToString;//编号->姓名----1-AAA
        return numPerson++;//1->2
    }
    
}

//dfs访问单个连通块,nowVisit是当前访问的编号
//head为头目,numMember是成员编号,totalVaule为连通块的总边权
void dfs(int nowVisit,int& head,int& numMember,int& totalVaule){
    
    
    numMember++;//
    vis[nowVisit]=true;//。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
    if(weight[nowVisit]>weight[head]){
    
    
        head=nowVisit;//当前节点的点权大于头目的点权,则更新头目
    }
    for(int i=0;i<numPerson;i++){
    
    //枚举所有人//。。。。。。。。。。。。。。。。。。。。。。。。。
        if(G[nowVisit][i]>0){
    
    //如果nowVisit能到达i
            totalVaule+=G[nowVisit][i];//连通块的总边权增加该边权
            G[nowVisit][i]=G[i][nowVisit]=0;//删除这条边,防止回头
            
            if(vis[i]==false){
    
    //如果nowVisit能到达i,如果i没被访问,则递归访问i//。。。。。。。。。。。。。。。。。。。
                dfs(i,head,numMember,totalVaule);//。。。。。。。。。。。。。。。。。。。。。。。。
            }
        }
    }
    
}

//遍历整个图,获得每个连通块的信息
void dfsTravel(){
    
    
    for(int i=0;i<numPerson;i++){
    
    //枚举所有人。。。。。。。。。。。。。。。。。。。。。。。。。。。。
         if(vis[i]==false){
    
    //如果i没有被昂访问。。。。。。。。。。。。。。。。。。。。。。。。。。。
         
             int head=i,numMember=0,totalVaule=0;//头目,成员数,总边权
             
             dfs(i,head,numMember,totalVaule);//遍历i所在的连通块//。。。。。。。。。。。。。。。。。
             
             if(numMember>2&&totalVaule>k){
    
    
                 //成员数>2并且总边权
                 //head人数为numMember
                 Gang[intToString[head]]=numMember;//搞了半天,这是在对map进行操作啊!!!!!!!!!//map<string,int>Gang;//head->人数
             }
         }
        
    }
    
}


int main()
{
    
    
    int w;
    string str1,str2;
    cin>>n>>k;//8 59
    for(int i=0;i<n;i++){
    
    
        cin>>str1>>str2>>w;//2个端点和点权//AAA BBB 10
        //----->点
        int id1=change(str1);//将str1转换为编号id1//AAA-1//-------1
        int id2=change(str2);//将str2转换为编号id2//BBB-2//-------2
        //点---->点权
        //邻接矩阵
        weight[id1]+=w;//id1的点权增加w
        weight[id2]+=w;//id2的点权增加w
        G[id1][id2]+=w;//边id1->id2的边权增加w
        G[id2][id1]+=w;//边id2->id1的边权增加w
    }
    dfsTravel();//是遍历整个图的所有连通块,获取Gang的信息
    
    cout<<Gang.size()<<endl;
    map<string,int>::iterator it;
    for(it=Gang.begin();it!=Gang.end();it++){
    
    
        cout<<it->first<<" "<<it->second<<endl;//map<string,int>Gang;//head->人数
    }
  return 0;
}

邻接矩阵C++实现:

#include <iostream>
#include <cstdio>
using namespace std;

#define maxn 100
#define INF 1xffffff    //预定于的最大值
int n, m;   //顶点数、边数
int g[maxn][maxn];      //邻接矩阵表示

void Init()
{
    
    
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n; j++)
        g[i][j] = 0;    //讲所有顶点度数置零,若为带权图,则置为INF
}
void Show() //打印邻接矩阵
{
    
    
    for(int i = 1; i <= n; i++)
    {
    
    
        for(int j = 1; j <= n; j++)
            cout << g[i][j] << " ";
        cout << endl;
    }
}
int main()
{
    
    
    int a, b;
    cout << "Enter n and m:";
    cin >> n >> m;
    while(m--)
    {
    
    
        cin >> a >> b;  //输入为边的始点、终点,若有权,还需输入权w
        g[a][b] = 1;    //a、b间存在边,将g[a][b]置1,若有权,则将其置为权值
        g[b][a] = 1;    //对于无向图,还要插入边(b,a)
    }
    Show();
    return 0;
}

邻接矩阵也可以用来表示加权图。例如,如果G=<V,E>是一个加权图,其权值函数为w,对于边(u,v)∈E,其权值w(u,v)就可以简单地存储在邻接矩阵的第u行第v列的元素中。如果边不存在,则可以在矩阵的相应元素中存一个NIL值,在很多问题中,对这样的元素赋0或∞会更为方便些。
在这里插入图片描述

 int w;
    string str1,str2;
    cin>>n>>k;//8 59
    for(int i=0;i<n;i++){
    
    
        cin>>str1>>str2>>w;//2个端点和点权//AAA BBB 10
        //----->点
        int id1=change(str1);//将str1转换为编号id1//AAA-1//-------1
        int id2=change(str2);//将str2转换为编号id2//BBB-2//-------2
        //点---->点权
        //邻接矩阵
        weight[id1]+=w;//id1的点权增加w
        weight[id2]+=w;//id2的点权增加w
        G[id1][id2]+=w;//边id1->id2的边权增加w
        G[id2][id1]+=w;//边id2->id1的边权增加w
    }

邻接矩阵法

用一维数组图中顶点的信息,用一个二维数组存储图中边的信息(各顶点之间的邻接关系)。存储顶点之间邻接关系的二维数组称为邻接矩阵。
在这里插入图片描述
对于带权图而言,若顶点vi,vj之间有边相连,则邻接矩阵中对应项存放着该边对应的权值,若顶点vi和vj不相连,则用∞来表示这两个顶点之间不存在边
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
网的邻接矩阵表示(网就是带权的图在这里插入图片描述

无向图

#include <stdio.h>
#include <string.h>
#define MaxVertexNum  100   //顶点数目最大值
typedef char VertexType;   //顶点的数据类型
typedef int EdgeType;     //带权图中边上权值的数据类型
 
typedef struct
{
    
    
	VertexType Vex[MaxVertexNum];   //顶点表
	EdgeType Edge[MaxVertexNum][MaxVertexNum];  //邻接矩阵,边表
	int vexnum, edgenum;    //图的顶点数和弧数
}MGraph;
 
/**********创建无向图**********/
void create_Graph(MGraph *G)
{
    
    
	int i, j;
	int start, end;  //边的起点序号、终点序号
	int numV, numE;
	int w;   //边上的权值
	printf("请输入所创建无向图的顶点数和边数(用空格隔开):");
	scanf_s("%d%d", &numV, &numE);
	G->vexnum=numV;
	G->edgenum=numE;
	
	printf("\n");
	//图的初始化
	for (i = 0; i < G->vexnum; i++)
	{
    
    
		for (j = 0; j < G->vexnum; j++)
		{
    
    
			if (i == j)
				G->Edge[i][j] = 0;
			else
				G->Edge[i][j] = 32767;
		}
	}
 
	//顶点信息存入顶点表
	for (i = 0; i < G->vexnum; i++)
	{
    
    
		printf("请输入第%d个顶点的信息:",i+1);
		scanf_s("%d", &G->Vex[i]);
	}
	printf("\n");
	//输入无向图边的信息
	for (i = 0; i < G->edgenum; i++)
	{
    
    
		printf("请输入边的起点序号,终点序号,权值(用空格隔开):");
		scanf_s("%d%d%d", &start, &end, &w);
		G->Edge[start -1][end -1] = w;
		G->Edge[end - 1][start - 1] = w;   //无向图具有对称性
	}
}
 
/***********打印出邻接矩阵*********/
void print_Matrix(MGraph G)
{
    
    
	int i, j;
	printf("\n图的顶点为:");
	for (i = 0; i < G.vexnum; i++)
		printf("%d ", G.Vex[i]);
	printf("\n输出邻接矩阵:\n");
	printf("\t");
	for (i = 0; i < G.vexnum; i++)
		printf("\t%8d", G.Vex[i]);
		
	for (i = 0; i < G.vexnum; i++)
	{
    
    
		printf("\n\n%8d", G.Vex[i]);
		for (j = 0; j < G.vexnum; j++)
		{
    
    
			if (G.Edge[i][j] == 32767)
				printf("\t%8s", "∞");
			else
				printf("\t%8d", G.Edge[i][j]);
		}
		printf("\n");
	}	
}
 
void main()
{
    
    
	MGraph G;
	create_Graph(&G);
	print_Matrix(G);
	system("pause");
}

在这里插入图片描述
有向图
代码基本是一样的,只是对称的位置上权值不一样

/***********创建有向图************/
void create_Graph2(MGraph *G)
{
    
    
	int i, j;
	int start, end;  //边的起点序号、终点序号
	int numV, numE;
	int w;   //边上的权值
	printf("请输入所创建有向图的顶点数和边数(用空格隔开):");
	scanf_s("%d%d", &numV, &numE);
	G->vexnum = numV;
	G->edgenum = numE;
	printf("\n");
	//图的初始化
	for (i = 0; i < G->vexnum; i++)
	{
    
    
		for (j = 0; j < G->vexnum; j++)
		{
    
    
			if (i == j)
				G->Edge[i][j] = 0;
			else
				G->Edge[i][j] = 32767;
		}
	}
	//顶点信息存入顶点表
	for (i = 0; i < G->vexnum; i++)
	{
    
    
		printf("请输入第%d个顶点的信息:", i + 1);
		scanf_s("%d", &G->Vex[i]);
	}
	printf("\n");
	//输入无向图边的信息
	for (i = 0; i < G->edgenum; i++)
	{
    
    
		printf("请输入边的起点序号,终点序号,权值(用空格隔开):");
		scanf_s("%d%d%d", &start, &end, &w);
		G->Edge[start - 1][end - 1] = w;   //有向图只在这里不一样
		//G->Edge[start -1][end -1] = w;
		//G->Edge[end - 1][start - 1] = w;   //无向图具有对称性

	}
}

深度优先遍历

/**********深度优先遍历*********/
int visitDFS[maxSize];
void DFS(MGraph G,int i)//某个顶点
{
    
    
	int j;
	visitDFS[i] = 1;//标记某个顶点访问过
	printf("%d ", G.Vex[i]);//顶点编号0-->1
	for (j = 0; j < G.vexnum; j++)//从某个顶点开始遍历图(每个顶点)
	//遍历每个的顶点范围大,需要条件有关系,没访问,不像v[i].size()
	//从u出发可以到达的所有顶点v
	//从v出发到达的所有顶点..
	//....
	{
    
    
		if (G.Edge[i][j] != 32767 && !visitDFS[j])
			DFS(G, j);//递归访问j
			
	}
}
 
void DFSTraverse(MGraph G)
{
    
    
	int i;
	//for (i = 0; i < G.vexnum; i++)
		//visitDFS[i] = 0;
		
	for (i = 0; i < G.vexnum; i++)//顶点数//遍历每个顶点
	{
    
    
		if (!visitDFS[i])
			DFS(G, i);//从某个顶点开始遍历图
	}
}


	//printf("请输入所创建有向图的顶点数和边数(用空格隔开):");
	//scanf_s("%d%d", &numV, &numE);
	//G->vexnum = numV;//顶点数

图的深度优先遍历DFS (邻接矩阵实现)
图的遍历是指从图中的某一顶点出发,
按照一定的策略访问图中的每一个顶点。每个顶点有且只能被访问一次。

它的遍历规则:先选择一个初始顶点,再规定一个方向,例如往右边一直遍历。于是就往右边一直走,把访问过的顶点做好标记,沿着右边访问完后,回溯到之前访问过的顶点,找找还有没有顶点没有访问的,当所有顶点被访问,完成遍历。

DFS是一个递归地将图中所有顶点访问的过程,
类似于树的前序遍历。

用邻接矩阵实现时需注意设置一个布尔类型的访问标志数组 visited

猜你喜欢

转载自blog.csdn.net/weixin_44769957/article/details/108949151