第三章 图论 No.6负环之01分数规划与特殊建图方式

这篇具有很好参考价值的文章主要介绍了第三章 图论 No.6负环之01分数规划与特殊建图方式。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

裸题:904. 虫洞

904. 虫洞 - AcWing题库
第三章 图论 No.6负环之01分数规划与特殊建图方式,AcWing算法提高课 课程记录,图论

// 虫洞是负权且单向边,道路是正权且双向边,题目较裸,判断有无负环即可
#include <iostream>
#include <cstring>
using namespace std;

const int N = 510, M = 6010;
int h[N], e[M], ne[M], w[M], idx;
int n, m, k;
int dis[N], cnt[N];
int q[N];
bool st[N];

void add(int x, int y, int d)
{
    e[idx] = y, ne[idx] = h[x], w[idx] = d, h[x] = idx ++ ;
}

bool spfa()
{
    int tt = 0, hh = 0;
    memset(cnt, 0, sizeof(cnt));
    memset(dis, 0, sizeof(dis));
    memset(st, 0, sizeof(st));
    for (int i = 1; i <= n; ++ i ) st[i] = true, q[tt ++ ] = i;
    while (hh != tt)
    {
        int x = q[hh ++ ];
        if (hh == N) hh = 0;
        st[x] = false;
        for (int i = h[x]; i != -1; i = ne[i])
        {
            int y = e[i];
            if (dis[y] > dis[x] + w[i])
            {
                cnt[y] = cnt[x] + 1;
                if (cnt[y] >= n) return true;
                dis[y] = dis[x] + w[i];
                if (!st[y]) 
                {
                    st[y] = true;
                    q[tt ++ ] = y;
                    if (tt == N) tt = 0;
                }
            }
        }
    }
    return false;
}

int main()
{
    int T;
    scanf("%d", &T);
    while (T -- )
    {
        memset(h, -1, sizeof(h));
        idx = 0;
        scanf("%d%d%d", &n, &m, &k);
        int x, y, d;
        for (int i = 0; i < m; ++ i )
        {
            scanf("%d%d%d", &x, &y, &d);
            add(x, y, d), add(y, x, d);
        }
        for (int i = 0; i < k; ++ i )
        {
            scanf("%d%d%d", &x, &y, &d);
            add(x, y, -d);
        }
        if (spfa()) puts("YES");
        else puts("NO");
    }
    return 0;
}

第三章 图论 No.6负环之01分数规划与特殊建图方式,AcWing算法提高课 课程记录,图论
这个==真的服,调半天,还有,邻接表的大小又设置错了


01分数规划:361. 观光奶牛

361. 观光奶牛 - AcWing题库
第三章 图论 No.6负环之01分数规划与特殊建图方式,AcWing算法提高课 课程记录,图论

在图论问题中,所有形如:某个部分之和除以某个部分之和最大的问题,被称为01分数规划,通常使用二分解决这类问题
根据题意,这道题的答案范围在 ( 0 , 1000 ] (0, 1000] (0,1000]中,我们需要二分这个区间找到答案
若点权之和/边权之和大于等于mid,则说明答案在 [ m i d , r ] [mid, r] [mid,r]之间
反之,点权之和/边权小于mid,则说明答案在 [ l , m i d ] [l, mid] [l,mid]之间
根据这个二段性,我们能二分出ans,使得边权之和/边权之和的最大值 = ans

现在的问题是check如何实现?
整理不等式,如下图:
第三章 图论 No.6负环之01分数规划与特殊建图方式,AcWing算法提高课 课程记录,图论

一个常用的技巧:若图中的环既有点权又有边权,那么可以将点权加到出边或者入边上
那么不等式的求和可以提到外面,结合这个技巧,将点权和边权结合
若一条边由x->y,权值为w,那么将其权值设置为 f x − m i d ∗ w f_x-mid*w fxmidw f x f_x fx为x的点权
问题就转换成了图中是否存在一个正环?
求正环只要修改三角不等式即可:dis[y] < dis[x] + w[i]

总结下:check判断图中是否存在一个环,其点权之和/边权之和大于等于mid,转换成图中是否存在一个正环(或权值和为0的环),若存在,则l = mid,否则r = mid,

  1. 思考题目的二段性
  2. 根据不等式重置边/点权
  3. 根据不等式判断题目的具体问题:负环/最小生成树/最短路
#include <iostream>
#include <cstring>
using namespace std;

const int N = 1010, M = 5010;
int h[N], e[M], ne[M], w[M], idx;
int f[N];
double dis[N];
int cnt[N]; bool st[N];
int q[N];

int n, m;

void add(int x, int y, int d)
{
    e[idx] = y, ne[idx] = h[x], w[idx] = d, h[x] = idx ++ ;
}

bool check(double mid)
{
    memset(dis, 0, sizeof(dis));
    memset(cnt, 0, sizeof(cnt));
    int tt = 0, hh = 0;
    
    for (int i = 1; i <= n; ++ i ) st[i] = true, q[tt ++ ] = i;
    while (hh != tt)
    {
        int x = q[hh ++ ];
        if (hh == N) hh = 0;
        st[x] = false;
        for (int i = h[x]; i != -1; i = ne[i])
        {
            int y = e[i];
            if (dis[y] <= dis[x] + f[x] - mid * w[i])
            {
                dis[y] = dis[x] + f[x] - mid * w[i];
                cnt[y] = cnt[x] + 1;
                if (cnt[y] >= n) return true;
                if(!st[y])
                {
                    st[y] = true;
                    q[tt ++ ] = y;
                    if (tt == N) tt = 0;
                }
            }
        }
    }
    return false;
}

int main()
{
    memset(h, -1, sizeof(h));
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; ++ i ) scanf("%d", &f[i]);
    int x, y, d;
    for (int i = 0; i < m; ++ i )
    {
        scanf("%d%d%d", &x, &y, &d);
        add(x, y, d);
    }
    
    double l = 0, r = 1000;
    while (r - l > 1e-4)
    {
        double mid = (l + r) / 2;
        if (check(mid)) l = mid;
        else r = mid;
    }
    
    printf("%.2lf\n", r);
    return 0;
}

debug:点权需要从数组1号下标开始读取


特殊建图与01分数规划+trick:1165. 单词环

1165. 单词环 - AcWing题库
第三章 图论 No.6负环之01分数规划与特殊建图方式,AcWing算法提高课 课程记录,图论

估算一下这题的数据量,如果按照题意建图,不仅爆空间还会爆时间,所以这题需要考虑其他建图方式
第三章 图论 No.6负环之01分数规划与特殊建图方式,AcWing算法提高课 课程记录,图论

题目给定的建图方式是:单词为点,若两单词能相连,那么边的权值为1
考虑新的建图方式,以单词的前两个字符为起点,最后两个字符为终点,建立一条有向边,权值为单词的长度。这种建图方式中,点的数量最多为26 * 26,边的数量为 1 0 5 10^5 105

其次,题目要求环中所有单词的长度之和 / 环中的单词数量最大,显然是01分数规划
二分答案,答案的范围是 ( 0 , 1000 ] (0, 1000] (0,1000],最大的答案为每个单词长度都是1000,而最小的答案0是取不到的,最小的情况应该是1,0用来表示无解
整理不等式,重新设置边权为 w i − 1 ∗ m i d w_i - 1 * mid wi1mid,1是由环中点的数量累加后(第二个式子)再把累加提到外面(第三个等式)得到的
check:每次根据mid判断图中是否存在正环或零环,若存在返回true,反之返回false

trick:如果spfa更新了很多次还没有结束循环,那么有极大概率可以认为图中存在环,这里设置阈值为10000(点数的十几倍),当循环次数超过该值时,直接认为图中存在环、
不过这样的trick在正规比赛中不会出现

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

const int N = 27 * 27, M = 1e5 + 10;
int h[N], e[M], ne[M], w[M], idx;
double dis[N];
int cnt[N], q[N];
bool st[N];

void add(int x, int y, int d)
{
    e[idx] = y, ne[idx] = h[x], w[idx] = d, h[x] = idx ++ ;
}

bool check(double mid)
{
    memset(dis, 0, sizeof(dis));
    memset(cnt, 0, sizeof(cnt));
    int tt = 0, hh = 0, count = 0;
    for (int i = 0; i < N - 1; ++ i ) q[tt ++ ] = i, st[i] = true;
    while (hh != tt )
    {
        int x = q[hh ++ ];
        if (hh == N) hh = 0;
        st[x] = false;
        for (int i = h[x]; i != -1; i = ne[i])
        {
            int y = e[i];
            if (dis[y] <= dis[x] + w[i] - mid)
            {
                cnt[y] = cnt[x] + 1;
                if (cnt[y] >= N) return true;
                if (++ count >= 10000) return true;
                dis[y] = dis[x] + w[i] - mid;
                if (!st[y])
                {
                    st[y] = true;
                    q[tt ++ ] = y;
                    if (tt == N) tt = 0;
                }
            }
        }
    }
    return false;
}

int main()
{
    int m;
    char str[1010];
    while (scanf("%d", &m), m)
    {
        memset(h, -1, sizeof(h));
        idx = 0;
        for (int i = 0; i < m; ++ i )
        {
            scanf("%s", str);
            int len = strlen(str);
            if (len >= 2)
            {
                int x = (str[0] - 'a') * 26 + str[1] - 'a';
                int y = (str[len - 2] - 'a') * 26 + str[len - 1] - 'a';
                add(x, y, len);
            }
        }
        
        double l = 0, r = 1000;
        while (r - l > 1e-4)
        {
            double mid = (l + r) / 2;
            if (check(mid)) l = mid;
            else r = mid;
        }
        
        if (r < 1e-4) puts("No solution");
        else printf("%.2lf\n", r);
    }
    return 0;
}

debug:dis数组的类型开成int,想着边的权值为整数,int就行,然而边权被重置,类型是浮点数文章来源地址https://www.toymoban.com/news/detail-635136.html

到了这里,关于第三章 图论 No.6负环之01分数规划与特殊建图方式的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 第三章 图论 No.4最小生成树的简单应用

    存在边权为负的情况下,无法求最小生成树 裸题:1140. 最短网络 1140. 最短网络 - AcWing题库 套个prim的板子即可 裸题:1141. 局域网 1141. 局域网 - AcWing题库 裸题,稀疏图,套个kruskal的板子就行 需要注意的是:题目给定的图可能存在多个连通块,若使用prim算法,需要对每个连通

    2024年02月14日
    浏览(41)
  • 第三章 图论 No.11二分图,匈牙利算法与点覆盖

    257. 关押罪犯 - AcWing题库 最大最小问题,一眼二分 答案的范围在 [ 1 , 1 e 9 ] [1, 1e9] [ 1 , 1 e 9 ] 之间,二分答案,check(mid) check:将所有权值大于mid的边进行二分,若能得到二分图,返回true,否则返回false 最终将得到最优解ans,所有大于ans的边组成的图为二分图,若是图中有边

    2024年02月12日
    浏览(30)
  • 第三章 图论 No.8最近公共祖先lca, tarjan与次小生成树

    O ( m l o g n ) O(mlogn) O ( m l o g n ) ,n为节点数量,m为询问次数,lca是一种在线处理询问的算法 自己也是自己的祖先 倍增: f a ( i , j ) fa(i, j) f a ( i , j ) 表示从i开始,向上走 2 j 2^j 2 j 步走到的点 j = 0,走到父节点 j 0,分两步走,先走到 2 j − 1 2^{j-1} 2 j − 1 步再走 2 j − 1 2^{

    2024年02月13日
    浏览(29)
  • 第三章 图论 No.9有向图的强连通与半连通分量

    连通分量是无向图的概念,yxc说错了,不要被误导 强连通分量:在一个有向图中,对于分量中的任意两点u,v,一定能从u走到v,且能从v走到u。强连通分量是一些点的集合,若加入其他点,强连通分量中的任意两点就不能互相递达 半连通分量:在一个有向图中,对于分量中

    2024年02月13日
    浏览(32)
  • 第三章 图论 No.5最小生成树之虚拟源点,完全图与次小生成树

    虚拟源点:1146. 新的开始 1146. 新的开始 - AcWing题库 与一般的最小生成树问题不同,本题需要在建立电站的电井之间建立电网,在两个电站之间建立电网需要花费金额,可以看成一条具有权值的边 但是建立电网的前提是:其中一个电井需要建立电站,建立电站也需要费用 已经

    2024年02月14日
    浏览(33)
  • 第三章 图论 No.3 flody之多源汇最短路,传递闭包,最小环与倍增

    flody的四个应用: 多源汇最短路 传递闭包 找最小环 恰好经过k条边的最短路 倍增 多源汇最短路:1125. 牛的旅行 1125. 牛的旅行 - AcWing题库 直径概念:同一连通块中,两个距离最远的点之间的距离 如何求直径?由于图中存在着两个连通块,所以直接对全图做一个flody,就能更

    2024年02月14日
    浏览(30)
  • 第三章 图论 No.2单源最短路之虚拟源点,状压最短路与最短路次短路条数

    dp是特殊的最短路,是无环图(拓扑图)上的最短路问题 1137. 选择最佳线路 1137. 选择最佳线路 - AcWing题库 对于每组测试数据,该重置的数据要重置,我没有重置idx,导致TLE 处理反向建图,还有一种扩展做法:虚拟源点 设置虚拟源点,与每个起点之间连接边权为0的边 原问题

    2024年02月14日
    浏览(36)
  • 第三章 搜索与图论(二)(最短路)

    1、对于稠密图,由于朴素版的dijkstra算法与边数无关使用这种算法的复杂度较低。稀疏图用堆优化版的算法;单源最短路中存在负权边用SPFA 算法通常较好;多源用floyd算法;  难点:如何建图,抽象为最短路问题。 由于稠密图用这种算法,邻接矩阵存图,注意把g初始化为

    2024年02月21日
    浏览(30)
  • 第三章 搜索与图论(二)——最短路问题

    源点表示起点,汇点表示终点 一些认识: m和 n 2 n^2 n 2 一个级别是稠密图,m和n一个级别是稀疏图 最短路问题不区分有向图与无向图,因为无向图是一种特殊的有向图,能解决有向图的最短路问题,就能解决无向图的最短路问题 起点确定,终点是除起点外的其他点 默认n表示

    2024年02月13日
    浏览(30)
  • C++算法之旅、06 基础篇 | 第三章 图论

    常用代码模板3——搜索与图论 - AcWing 尽可能往深处搜,遇到叶子节点(无路可走)回溯, 恢复现场继续走 数据结构:stack 空间:需要记住路径上的点, (O(h)) 。 ⭐ BFS使用空间少; 无最短路 性质 每个DFS一定对应一个 搜索树 ;要考虑用什么 顺序 遍历所有方案;DFS就是递

    2024年02月10日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包