Part 一 、 二、
邻接矩阵 与邻接表(邻接链表)
Time Limit: 1000 ms Memory Limit: 65536 KiB
Problem Description
解决图论问题,首先就要思考用什么样的方式存储图。但是小鑫却怎么也弄不明白如何存图才能有利于解决问题。你能帮他解决这个问题么?
Input
多组输入,到文件结尾。
每一组第一行有两个数n、m表示n个点,m条有向边。接下来有m行,每行两个数u、v代表u到v有一条有向边。第m+2行有一个数q代表询问次数,接下来q行每行有一个询问,输入两个数为a,b。
注意:点的编号为0~n-1,2<=n<=500000 ,0<=m<=500000,0<=q<=500000,a!=b,输入保证没有自环和重边
Output
对于每一条询问,输出一行。若a到b可以直接连通输出Yes,否则输出No。
Sample Input
2 1
0 1
2
0 1
1 0
Sample Output
Yes
No
Hint
Source
lin
以下该think总结转载于xxx博主 链接, 用以学习 。
还有学姐 xxx 链接
THINK:图的表示
(1)邻接矩阵
用下标代表点的标号,以二维数组储存数值表示边的存在与否,或者边的长度大小。
(2 )邻接表
一、邻接表
邻接表是图的一种链式存储结构。
邻接表中,对图中每个顶点建立一个单链表,第i个单链表中的结点表示依附于顶点Vi的边(对有向图是以顶点Vi为尾的弧)。
邻接表的处理方法是这样的:
(1)图中顶点用一个一维数组存储,当然,顶点也可以用单链表来存储,不过,数组可以较容易的读取顶点的信息,更加方便。
(2)图中每个顶点vi的所有邻接点构成一个线性表,由于邻接点的个数不定,所以,用单链表存储,无向图称为顶点vi的边表,有向图则称为顶点vi作为弧尾的出边表。
二、无向图的邻接表
三、有向图的邻接表和逆邻接表
(一)在有向图的邻接表中,第i个单链表链接的边都是顶点i发出的边。
(二)为了求第i个顶点的入度,需要遍历整个邻接表。因此可以建立逆邻接表。
(三)在有向图的逆邻接表中,第i个单链表链接的边都是进入顶点i的边。
另外,十字链表:
对于有向图来说,邻接表是有缺陷的。入度和出度问题不能同时解决。十字链表,就是把邻接表和逆邻接表结合起来的,这样既容易找到以v为尾的弧,也容易找到以v为头的弧,因而比较容易求得顶点的出度和入度。
四、邻接表小结
◆ 设图中有n个顶点,e条边,则用邻接表表示无向图时,需要n个顶点结点,2e个表结点;用邻接表表示有向图时,若不考虑逆邻接表,只需n个顶点结点,e个边结点。
◆ 在无向图的邻接表中,顶点vi的度恰为第i个链表中的结点数。
◆ 在有向图中,第i个链表中的结点个数只是顶点vi的出度。在逆邻接表中的第i个链表中的结点个数为vi的入度。
◆ 建立邻接表的时间复杂度为O(n+e)。
五、两者使用途径的区别:
如果图中边的数目远远小于n2称作稀疏图,这是用邻接表表示比用邻接矩阵表示节省空间;
如果图中边的数目接近于n2,对于无向图接近于n*(n-1)称作稠密图,考虑到邻接表中要附加链域,采用邻接矩阵表示法为宜。
在空间允许情况下:
在有向图中求顶点的度采用邻接矩阵比采用邻接表表示更方便
邻接表表示中第i个边表上的结点个数就是顶点Vi的出度,求入度较困难,需遍历个顶点的边表
逆邻接表表示中第i个边表上的结点个数就是顶点Vi的入度,求出度较困难,需遍历个顶点的边表
在邻接矩阵中求边的数目必须检测整个矩阵,所消耗的时间是O(n)
在邻接表中求边的个数,只要对每个边表计数即可求得所消耗的时间是O(n+e)
附加:简易的邻接表储存图的oj 题。代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct node //数据太大,不能用二维数组
{
int data;
struct node *next;
}node;
node *a[500000], *p;
int main()
{
int n,m,i,u,v,q;
while(~scanf("%d %d", &n, &m))
{
for(i=0; i < 500000; i++)
a[i] = NULL; //初始化节点
while(m--)
{
scanf("%d %d", &u, &v);
if(a[u] == NULL)
{
p = (node *)malloc(sizeof(node)); //第一次连,直接连
p->data = v;
p->next = NULL;
a[u] = p;
}
else
{
p = (node *)malloc(sizeof(node)); //已经连通,插入,
p->data = v;
p->next = a[u]->next;
a[u]->next = p;
}
}
scanf("%d",&q);
while(q--)
{
int flag = 0;
scanf("%d %d", &u, &v);
p = a[u];
while(p != NULL)
{
if(p->data == v)
{
flag = 1;
break;
}
p = p->next;
}
if(flag == 1)
printf("Yes\n");
else
printf("No\n");
}
}
return 0;
}
/***************************************************
User name: i am your lover
Result: Accepted
Take time: 56ms
Take Memory: 5988KB
Submit time: 2018-08-
****************************************************/
图的基本存储的基本方式一
Time Limit: 1800 ms Memory Limit: 65536 KiB
Input
多组输入,到文件结尾。
每一组第一行有两个数n、m表示n个点,m条有向边。接下来有m行,每行两个数u、v代表u到v有一条有向边。第m+2行有一个数q代表询问次数,接下来q行每行有一个询问,输入两个数为a,b。
注意:点的编号为0~n-1,2<=n<=5000 ,n*(n-1)/2<=m<=n*(n-1),0<=q<=1000000,a!=b,输入保证没有自环和重边
Output
对于每一条询问,输出一行。若a到b可以直接连通输出Yes,否则输出No。
Sample Input
2 1
0 1
2
0 1
1 0
Sample Output
Yes
No
Hint
Source
lin
临街矩阵
#include <stdio.h>
#include <string.h>
bool map[5000][5000];//标记路径,bool类型在存储二值变量,
//或者说只有真假时,更具优势,
//因为只有0和1即false和true,省空间
int main()
{
int n, m, u, v;
while(~scanf("%d%d",&n,&m))
{
memset(map, 0, sizeof(map));
for(int i = 0; i < m; i++)
{
scanf("%d%d",&u,&v);
map[u][v] = 1;
}
int q;
scanf("%d",&q);
while(q--)
{
int a, b;
scanf("%d%d",&a,&b);
if(map[a][b])
{
printf("Yes\n");
}
else printf("No\n");
}
}
return 0;
}
part 3. 三 结构体+数组(边集数组)
Time Limit: 1000 ms Memory Limit: 65536 KiB
Problem Description
解决图论问题,首先就要思考用什么样的方式存储图。但是小鑫却怎么也弄不明白如何存图才能有利于解决问题。你能帮他解决这个问题么?
Input
多组输入,到文件结尾。
每一组第一行有两个数n、m表示n个点,m条有向边。接下来有m行,每行两个数u、v、w代表u到v有一条有向边权值为w。第m+2行有一个数q代表询问次数,接下来q行每行有一个询问,输入一个数为a
注意:点的编号为0~n-1,2<=n<=500000 ,0<=m<=500000,0<=q<=500000,u!=v,w为int型数据。输入保证没有自环和重边
Output
对于每一条询问,输出一行两个数x,y。表示排序后第a条边是由x到y的。对于每条边来说排序规则如下:
权值小的在前。
权值相等的边出发点编号小的在前
权值和出发点相等的到达点编号小的在前
注:边的编号自0开始
Sample Input
4 3
0 1 1
1 2 2
1 3 0
3
0
1
2
Sample Output
1 3
0 1
1 2
Hint
Source
lin
了解下qsort函数
C/C++中有一个快速排序的标准库函数 qsort ,在stdlib.h 中声明,其原型为:
void qsort(void base, int nelem, unsigned int width, int ( pfCompare)( const void , const void ));
使用该函数,可以对任何类型的一维数组排序。该函数参数中,base 是待排序数组的起始地址,nelem 是待排序数组的元素个数,width 是待排序数组的每个元素的大小(以字节为单位),最后一个参数 pfCompare 是一个函数指针,它指向一个“比较函数”。排序就是一个不断比较并交换位置的过程。qsort 如何在连元素的类型是什么都不知道的情况下,比较两个元素并判断哪个应该在前呢?答案是,qsort 函数在执行期间,会通过pfCompare指针调用一个 “比较函数”,用以判断两个元素哪个更应该排在前面。这个“比较函数”不是C/C++的库函数,而是由使用qsort 的程序员编写的。在调用qsort 时, 将“比较函数”的名字作为实参传递给pfCompare。程序员当然清楚该按什么规则决定哪个元素应该在前,哪个元素应该在后,这个规则就体现在“比较函数”中。
qsort 函数的用法规定,“比较函数”的原型应是:int 函数名(const void * elem1, const void * elem2);该函数的两个参数,elem1 和elem2,指向待比较的两个元素。也就是说, * elem1 和* elem2 就是待比较的两个元素。该函数必须具有以下行为:
1) 如果 * elem1 应该排在 * elem2 前面,则函数返回值是负整数(任何负整数都行)。
2) 如果 * elem1 和* elem2 哪个排在前面都行,那么函数返回0
3) 如果 * elem1 应该排在 * elem2 后面,则函数返回值是正整数(任何正整数都行)。
以下代码原作者忘了谁了 哈哈哈
以下为AC 代码 当做学习整理
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define Max 591000
struct node
{
int u;//记录端点
int v;//记录端点
int w;//记录权值
}s[500010];
int cmp(const void *a, const void *b)
{
struct node *c = (struct node *)a;
struct node *d = (struct node *)b;
if(c->w != d->w) return c->w - d->w;
else if (c->u != d->u)
return c->u - d->u;
else
return c->v - d->v;
}
int main()
{
int i,n,m,t,q;
while(~scanf("%d %d",&n,&m))
{
memset(s,Max, sizeof(struct node)); //对数组初始化防止影响结果
for(i = 0; i < m; i++)
{
scanf("%d %d %d",&s[i].u, &s[i].v, &s[i].w);
}
qsort(s,m,sizeof(s[0]),cmp);
scanf("%d",&q);
while(q--)
{
scanf("%d",&t);
printf("%d %d\n",s[t].u, s[t].v);
}
}
return 0;
}