QBXT 2018春季DP&图论班 2018.4.29 --- 图论基础

*钟皓曦(长者、钟百万)老师授课*

图:由边集E和点集V构成。

有向图:含有至少一条有向边的图。

无向图:只含有无向边的图。

→例:左图是有向图,因为其含有有向边。

右图是有向图。虽然无向边相当于两条有向边,但无向边的两条有向边的权值相同,而右图的两条有向边权值不一定相同。

路径:点和边组成的序列。

简单路径:序列中不出现重复点与重复边。

→例:下图用红色标记的就是一条简单路径。

树:由n个点,n-1条边组成的无向图。

→结论:一棵n个点的树只有n-1条边。

  →证明:每连一条边就相当于连通了两点,共需要n-1条边将n个点连通。

外向树:有从根向外趋势的有向树。

内向树:有从根向内趋势的有向树。

→例:左图为外向树,右图为内向树。

↓而上面的树既是外向树(左点为根时),又是内向树(右点为根时)。下面的树既不是外向树,也不是内向树。

基环树(环套树、树套环、章鱼图):任何无向树上连一条边,就会形成一棵基环树。

→性质:①有n条边。

    ②有且只有一环。

点仙人掌:图上任一点最多在一个环中。

 →例:不是点仙人掌

 

边仙人掌:图上任一边最多在一个环中。

 →例:不是边仙人掌

→P.S.:有关基环树和仙人掌的题目一般是DP,图论很少涉及。

DAG(有向无环图):没有环的有向图。

→结论:把有向环中的任一边反向,就会形成DAG。

二分图:将一个图中的点分成两个集合,图中所有的边都是从一个集合指向另一个集合,且两个集合内部都各没有边。

→例:

→结论:

①树是一个二分图。(将奇数层的点归为一个集合,偶数层的点归为一个集合,所有点都是从奇数层向偶数层连边)

方格图(网格图)也是一个二分图。

将方格染成黑白两色,将相邻两方格之间连边。可知所有边都是一端为白格、一端为黑格,将原图分成了两部分。

红色为边。

 

补图:把原有边删去,把原来没有的边加上,两图互为补图。

→例:

二分图的补图:左集合中的点各自之间都有边,右集合也是这样。

 互为补图。对二分图的补图再求一次补图,可将问题转化为二分图问题。

存图方式:

→邻接矩阵:

 

#include<cstdio>
#include<cstdlib>
#include<cstring>

using namespace std;

int main()
{
    return 0;
}

int n,m;

int map[1000][1000];

for (int a=1;a<=m;a++) {
    cin >> s >> e >> d;
    map[s][e] = d;
    map[e][s] = d;
}

memset(map,-1,sizeof(map));
memset(map,0,sizeof(map));
//memset不是你想memset啥就memset啥 不能memset(-2/1/2...)啥的 
memset(map,0x7f,sizeof(map));//0x7f7f7f7f
//最好不要memset(0x7f) 因为这样容易爆int 
memset(map,0x3f,sizeof(map));//0x3f3f3f3f
//常memset(0x3f),这样不会爆int 
邻接矩阵

 

→前向星:(这么恶心的东西就直接上长者的代码了)

 

#include<algorithm>

pair<int,int> ed[maxm];

cin >> n >> m;
for (int a=1;a<=m;a++) {
    cin >>  s >> e;
    ed[a] = make_pair(s,e);//s=起点 e=终点 
}
sort(ed+1,ed+m+1);
//排序 

int first[maxn];

for (int a=m;a>=1;a--)
    first[ed[a].first] = a;
//倒序更新,最后更新完的first[]数组一定代表每个点所连的第一条边 

first[n+1] = m+1;
for (int a=n;a>=1;a--)
    if (first[a] == 0) first[a] = first[a+1];
//将没有边的点的first数组直接指向它下一个点的第一条边。 
    
for (int a=first[s];a<first[s+1];a++)
    cout << ed[a].first << ed[a].second;


//zhe li kai shi shi bian biao

struct edge {
    int e;
    edge *next;
}*v[maxn],ed[maxm];

void add_edge(int s,int e) {
    en++;
    ed[en].next = v[s];v[s]=ed+en;v[s]->e = e;
}

for (edge *e=v[s];e;e=e->next)
    cout << e->e;


//er fen cha zhao
int z[maxn];

sort(z+1,z+n+1);
//cha xun x shi fou cun zai

int l=0,r=n;
while (l+1!=r) {
    int m=(l+r)/2;
    if (z[m]>=x) r=m;
    else l=m;
}
前向星 --- by zhx

 

 

→邻接表(边表)

 

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct node
{
    int to,nxt,w;
}e[M<<1];
int head[N],tot;
void add(int u,int v,int w)
{
    e[++tot]=(node){to,head[u],w};
    head[u]=tot;
}
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        add(u,v,w);
        add(v,u,w);//无向图存正反两条边 
    }
} 
邻接表

 

          优点                       缺点

 

邻接矩阵:O(1)定位、好写、快、支持动态加边。   O(n^2)空间(适用于n<=1000的情况)、枚举以u为起点的边O(n)。

 

前向星:比较快速地定位一条边是否存在O(logm)   难写、加边O(mlogn)、不支持动态加边。

    (唯一用处,因为边集已是有序的,      

    所以可以二分查找)

邻接表(边表):比较好写,遍历以u为起点的边最快 不能快速判断某边是否存在O(m)、链表不能排序,必须枚举。

转载于:https://www.cnblogs.com/Loi-Brilliant/p/8998889.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值