观光奶牛 详细题解

这篇具有很好参考价值的文章主要介绍了观光奶牛 详细题解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

#T3 #SPFA判断正/负环 #二分查找
为啥现在突然发出来:翻自个笔记发现这篇写的挺好hhh

361. 观光奶牛 - AcWing题库

给定一张 \(L\) 个点、\(P\) 条边的有向图,每个点都有一个权值 \(f[i]\),每条边都有一个权值 \(t[i]\)

求图中的一个环,使“环上各点的权值之和”除以“环上各边的权值之和”最大。

输出这个最大值。

注意:数据保证至少存在一个环。

输入格式

第一行包含两个整数 \(L\)\(P\)

接下来 \(L\) 行每行一个整数,表示 \(f[i]\)

再接下来 \(P\) 行,每行三个整数 \(a,b,t[i]\),表示点 \(a\)\(b\) 之间存在一条边,边的权值为 \(t[i]\)

输出格式

输出一个数表示结果,保留两位小数。

数据范围

\(2 \le L \le 1000\),
\(2 \le P \le 5000\),
\(1 \le f[i],t[i] \le 1000\)

输入样例:

5 7
30
10
10
5
10
1 2 3
2 3 2
3 4 5
3 5 2
4 5 5
5 1 3
5 2 2

输出样例:

6.00

思路

求有向图的一个环, 使“环上各点的权值之和”除以“环上各边的权值之和”最大。输出最大值。即 \(\frac{\sum f[i]}{\sum t[i]}\)最大。

这种类似的问题就是01分数规划问题, 有模板解法。其结果是存在上下界的, 最小因为权值都是正整数, 故为\(0\)(开区间), 最大值则是分母为\(L\), 分子最大\(1000*L\) , 故最大值为\(1000\)

而其两段性是很显然的, 设任取中间一个值\(mid\) , 判断能否找到一个环的\(\frac{\sum f[i]}{\sum t[i]}\)值大于\(mid\), 如果不能, 说明图中最大值小于\(mid\), 继续在 \((0,mid]\) 中搜索。若搜得到, 就说明图中最大值存在与\((mid, 1000L]\) 之间, 继续缩小范围。最终会找到满足条件的最大值。

判断是否存在一个环满足\(\frac{\sum f[i]}{\sum t[i]}>mid\) 先做一个数学上的变换:
\(\sum f[i] - \sum t[i]*mid > 0\)
把求和符号提出可得等价式子:\(\sum \{f[i] - t[i]*mid\}>0\)
当我们把这求和里面那块看做从其他点到\(i\)点的边, 那么这个式子的含义就是, 存在一个环, 它的边权和大于0。既然小于0的叫做负环, 那大于0就叫做正环。

求正环的操作跟负环类似, 只需要把求最小值换成求最大值即可。

求正负环还有个优化操作, 当寻找到一定次数之后可以提前结束以避免超时, 即定义一个变量\(count\) 记录当前搜索次数, 超过一般 \(2n/3n\)时就提前返回。

也可以把队列改成栈, 这样搜索模式为深度优先, 会更多的去更新刚入栈的点, 让\(cnt\)数组累加的更快。文章来源地址https://www.toymoban.com/news/detail-711574.html

代码

#include <iostream>
#include <cstring>
#include <algorithm>
#include <string>
#include <cmath>
using namespace std;
const int N = 1e3 + 10, M = 1e5 + 10;
int n,m;
int h[N], e[M], w[M], ne[M], idx;
int f[N];
int q[N], cnt[N];
double dist[N];
bool st[N];

void add(int a, int b, int c) {e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;}

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

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

到了这里,关于观光奶牛 详细题解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • 【题解】二分查找-I、二维数组中的查找

    题目链接:二分查找-I 解题思路:遍历 代码如下: 这种解题思路很明显没有很好的利用题目中强调的数组是升序的,既然是升序,那肯定前半部分偏小,后半部分偏大,如果我们能知道应该去前半部分还是后半部分寻找target,效率相对就提升很多了,于是我们有了下面的分

    2024年02月14日
    浏览(43)
  • 图论:最短路(dijkstra算法、bellman算法、spfa算法、floyd算法)详细版

    终于是学完了,这个最短路我学了好几天,当然也学了别的算法啦,也是非常的累啊。 话不多说下面看看最短路问题吧。 最短路问题是有向图,要求的是图中一个点到起点的距离,其中我们要输入点和点之间的距离,来求最短路。 下面分为几类题目: 单源汇最短路--一个起

    2024年01月21日
    浏览(44)
  • [USACO11MAR] Brownie Slicing G题解(二分+二维前缀和+矩阵分割)

    题目地址 P3017 [USACO11MAR] Brownie Slicing G 二分最大化最小值 切割思路: 一行一行进行切割,如果这一行可以切割出b块大于等于mid的块,就开始切割下一行 如果无法切割出b块,就把正在切割的行与下一行拼起来一起切割 最后通过能切割出b块的水平块块够不够a条来判断m是否合

    2024年02月07日
    浏览(39)
  • 【洛谷 P1024】[NOIP2001 提高组] 一元三次方程求解 题解(数学+二分答案)

    有形如: a x 3 + b x 2 + c x + d = 0 a x^3 + b x^2 + c x + d = 0 a x 3 + b x 2 + c x + d = 0 这样的一个一元三次方程。给出该方程中各项的系数( a , b , c , d a,b,c,d a , b , c , d 均为实数),并约定该方程存在三个不同实根(根的范围在 − 100 -100 − 100 至 100 100 100 之间),且根与根之差的绝对值

    2024年02月06日
    浏览(36)
  • 题解 | #判断两个IP是否属于同一子网# 简单好理解

    题解 | #合并两个排序的链表# import java.util.*;/* * public class ListNode { * int val; * ListNode next =   题解 | #高精度整数加法# const rl = require(\\\"readline\\\").createInterface({ input: process.stdin   二本电气工程及其自动化投春招 听劝抗压 求指点 怎么修改   题解 | #查找两个字符串a,b中的最长公共子

    2024年03月24日
    浏览(75)
  • 【二分查找】详细图解

    目录 一.什么是二分查找法? 二.算法要求 三.算法思想 图解(要找的数k的值为3)  参考代码 二分查找也称折半查找(Binary Search),它是一种效率较高的查找方法。但是,折半查找要求线性表必须采用顺序储存结构,而且表中元素按有序排列。  1.必须是 有序排列。

    2024年02月10日
    浏览(84)
  • 二分(折半查找)详细解答(边界条件终止条件等等详细解释)

    刷 Leetcode 总能遇到关于二分的题目,但是之前也只是草草地了解一下,每次在使用的时候都需要找模板,要不然就需要对于边界条件进行调试,着实是很麻烦!!! 首先来简单介绍一下二分:二分查找也称折半查找(Binary Search),它是一种效率较高的查找方法。但是,折半

    2024年02月05日
    浏览(53)
  • 【信息奥赛题解】四平方和(详细分析题解 & C++ 代码)

    摊主的个人技术博客:https://rickyxcoder.top/ 🧑🏻‍💻 备用站点:https://rickyxcoder.gitee.io/ 🚀 题目浏览 【题目名称】四平方和 【题目描述】 四平方和定理,又称为拉格朗日定理: 每个正整数都可以表示为至多 4 4 4 个正整数的平方和。 如果把 0 0 0 包括进去,就正好可以表示

    2023年04月09日
    浏览(37)
  • 找负环(图论基础)

    环内路径上的权值和为负。 两种基本的方法 统计每一个点的入队次数,如果一个点入队了n次,则说明存在负环 统计当前每个点中的最短路中所包含的边数,如果当前某个点的最短路所包含的边数大于等于n,也说明存在负环 实际上两种方法是等价的,都是判断是否路径包含

    2024年02月20日
    浏览(27)
  • 算法提高-图论- 负环

    本博客主要介绍spfa求负环 一般用第二种方法 第一种方法如果每个点入队n次,每次入队也要遍历n次,那么时间复杂度就是n 2 第二种方法时间复杂度是n,只要发现最短路边数=n就说明有环了 一篇很好的博客,介绍了求负环的常用方法和原理 这是一个01规划 + 图论问题 判断负

    2024年02月11日
    浏览(46)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包