2021-01-23

搜索

一.三种图的存储方式
1.邻接矩阵:适合稠密图,但是十分浪费空间,并且存储的时间复杂度高
2.邻接表适用于规模大的稀疏图,存储复杂度为O(V+E)
使用STL的vecto来存储

//定义边
struct edge {
    int from, to, w;
    edge(int a, int b, int c) { from = a; to = b; w = c; }
};
vector<edge>e[MAXN];
//初始化
for (int i = 1; i <= n; i++) {
    e[i].clear();
}
//存边
e[a].push_back(edge(a, b, c));
//检索
for (int i = 0; i < e[u].size(); i++) {
    。。。。
}

3.链式前向星
直接上代码看操作过程吧

const int MAXN = 1e6 + 5;
struct Egde {
    int to, next, w;                  //边:终点to、权值w、下一个点next。起点放在head[]中
}edge[MAXN];
int head[MAXN];                       //head[u]指节点u的第一个边的存储位置
int cnt;                              //记录edge[]的末尾位置,新加入的边放在末尾
void init() {                         //初始化
    for (int i = 0; i < MAXN; ++i) {
        edge[i].next = -1;            //-1表示结束,没有下一个边
        head[i] = -1;                 //-1表示不存在从节点i出发的边
    }
    cnt = 0;
}
void addegde(int u, int v, int w) {
    edge[cnt].to = v;
    edge[cnt].w = w;
    edge[cnt].next = head[u];          //指向节点u上一次存的边的位置
    head[u] = cnt++;                   //更新节点u最新边的存放位置:就是在edge的末尾
}
//遍历节点i的所有邻居  ~i也可以写成i!=-1;
for (int i = head[u]; ~i; i = edge[i].next) {

}

链式前向星存储效率高能储存多重边,但不方便做删除操作

二.拓扑排序(在图中就是求有先后关系的点之前的排序)
出度:以点u为起点的边的数量称为u的出度。
入读:以点v为终点的边的数量成为v的入度
在这里插入图片描述
可以看出1点要排在2前面,2要排在3前面,3又要排在1前面。显然是不合理的,所以一个图能进行拓扑排序的一个充要条件就是它是一个有向无环图(DAG)

1.基于BFS的拓扑排序
步骤:(1)先找入度为0(最后的一个点)的点,如有多个,无先后关系,直接放入队列中即可,找不早入度为0就说这个不是DAG,return false;
(2)弹出队首a,把a的所有邻居点入度-1,把入度为0的邻居点入队
(3)继续(1)(2)操作,直到队列为空。
在这里插入图片描述
Q是BFS的队列
(a)进a,c,Q={a,c}
(b)弹出a,进b。Q={c,b}
(c)弹出c,Q={b}
(d)弹出b,进d,Q={d}
(e)弹出d,Q={}
结果为acbd;
2.题目hdu-3342

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<string>
using namespace std;
int mp[105][105];
int in[105];//表示入度
int main(){
    int n, m;
    while (~scanf("%d%d", &n, &m) && n && m) {
        memset(mp, 0, sizeof(mp));
        memset(in, 0, sizeof(in));
        for (int i = 1; i <= m; i++) {
            int a, b;
            scanf("%d%d", &a, &b);
            if (!mp[a][b]) {
                mp[a][b] = 1;
                in[b]++;
            }
        }
        int flag = 0;
        for (int i = 0; i < n; i++) {//编号0~n-1
            int j;
            for (j = 0; j < n; j++) {
                if (in[j] == 0)      //不存在入度为0的点,说明不是有向无环图,必然存在不合法关系
                    break;
            }
            if (j == n) {
                flag = 1;
                break;
            }
            else {
                in[j]--;                      //将j点删除后继续判断其他点
                for (int k = 0; k < n; k++) {
                    if (mp[j][k] == 1) {
                        in[k]--;
                    }
                }
            }
        }
        if (flag)printf("NO\n");
        else printf("YES\n");
    }
    return 0;
}

3.输出字典序最小的拓扑排序
hdu-1285
思路:优先队列+BFS拓扑排序
代码略

三.
1.二分图染色
给一个博客链接二分图染色这里就不过多的阐述了

2.题目hdu-5285

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<string>
using namespace std;
const int MAXN = 1e5 + 5;
int n, m, cnt;
int head[MAXN], color[MAXN], num[2];
bool vis[MAXN], flag;
struct EDGE {              //使用链式前向星来找相邻边更方便
    int v, next;
}e[MAXN*2];
void Add(int u, int v) {
    e[cnt].v = v;
    e[cnt].next = head[u];
    head[u] = cnt++;
}
void dfs(int u, int col) {
    vis[u] = true;
    color[u] = col;                     //开始染色
    num[col]++;
    for (int i = head[u]; i != -1; i = e[i].next) {
        int v = e[i].v;
        if (vis[v]) {                  //如果v点染过色
            if (color[v] == color[u]) {
                flag = true;
                return;
            }
        }
        else {
            dfs(v, col ^ 1);          //颜色01取反
        }
    }
    return;
}
int main(){
    int t;
    scanf("%d", &t);
    while (t--) {
        memset(head, -1, sizeof(head));
        memset(color, -1, sizeof(color));
        memset(vis, false, sizeof(vis));
        cnt = 0;
        flag = false;
        scanf("%d %d", &n, &m);
        for (int i = 0; i < m; i++) {
            int u, v;
            scanf("%d %d", &u, &v);
            Add(u, v);                 
            Add(v, u);                //不执行这个操作,在v点就无法判断u点
        }
        if (n < 2) {                 //少于两个人
            printf("Poor wyh\n");
            continue;
        }
        if (m == 0) {                //全都认识
            printf("%d 1\n", n - 1);
            continue;
        }
        int ans = 0;
        for (int i = 1; i <= n; i++) {
            if (!vis[i]) {
                memset(num, 0, sizeof(num));
                dfs(i, 0);
                if (flag)break;
                ans += max(num[0], num[1]);   //一个人对应的两种情况,要么颜色0,要么颜色1
            }
        }
        if (flag)
            printf("Poor wyh\n");
        else
            printf("%d %d\n", ans, n - ans);
    }
    return 0;
}

3.dfs剪枝题
A Funny Bipartite Graph

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<string>
#include<vector>
using namespace std;
#define Min(a,b) (a>b?b:a)
const int inf = 0x3f3f3f3f;
int res;
int n, num[35], vis[35], b[35], p[110][4];
vector<int>v[35], vec[35];
char s[35][35], a[35][35];
void init();
void dfs(int step, int ans);
int main() {
    for (int i = 1; i <= 100; i++) {
        p[i][0] = 1;
        for (int j = 1; j <= 3; j++) {       //p[i][j]存L的权值i的j次方
            p[i][j] = p[i][j - 1] * i;
        }
    }
    int T;
    scanf("%d", &T);
    while (T--) {
        init();
    }
    return 0;
}
void init() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        v[i].clear();
        vec[i].clear();
        vis[i] = 0;
    }
    res = inf;
    for (int i = 1; i <= n; i++) {
        scanf("%s", s[i] + 1);
    }
    for (int i = 1; i <= n; i++) {
        scanf("%s", a[i] + 1);
    }
    for (int i = 1; i <= n; i++) {
        scanf("%d", &b[i]);
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            if (s[i][j] == '1') {
                v[j].push_back(i);
            }
            if (a[i][j] == '1') {
                vec[i].push_back(j);
            }
        }
    }
    for (int i = 1; i <= n; i++) {
        if (v[i].size() == 0) {
            puts("-1");
            return;
        }
    }
    dfs(1, 0);
    if (res == inf)puts("-1");
    else printf("%d\n", res);
}
void dfs(int step, int ans) {
    if (ans > res)return;           //继续向下搜也不能得到比res更小的值
    if (step == n + 1) {
        res = Min(res, ans);
        return;
    }
    for (int i = 0; i < v[step].size(); i++) {
        int to = v[step][i];
        if (vis[to])continue;
        else {
            for (int j = 0; j < vec[to].size(); j++) {
                vis[vec[to][j]]++;   //将不能同时存在的标记一下
            }
            int f = 0;
            if (num[to] == 0)f = 0;
            else f = p[b[to]][num[to]];
            num[to]++;
            dfs(step + 1, ans + p[b[to]][num[to]] - f);
            num[to]--;                                   //回溯
            for (int j = 0; j < vec[to].size(); j++) {
                vis[vec[to][j]]--;
            }
        }
    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值