首页 > 其他分享 >题解:[USACO07DEC] Sightseeing Cows G

题解:[USACO07DEC] Sightseeing Cows G

时间:2024-12-06 22:13:12浏览次数:4  
标签:int 题解 mid double Cows include 1010 USACO07DEC dis

洛谷P2868

题目大意

有个 $n$ 个点,$m$ 条边的有向图,点有点权,边有边权。

现在要找出一个环,使得点权和与边权和的比值最大。

思路

既然说要使得点权和与边权和的比值最大,那么就会想到 $01$ 分数规划。二分答案就不用说了,重点是这个 $check$ 函数。

$01$ 分数规划的板子中要检查的是你选出来的最优的那几个 $a_i-b_i \cdot p$ 之和是否大于 $0$。

那么我们类比一下,想想现在怎么检查:

  • 我们可以寻找环,看看所选环中 $a_i-b_i \cdot p$ 的之和是否大于 $0$。
  • 更简单一点的方式:转换为负权环问题(其实就是把权值取反)。
  • 然后 板子题 $*$ $2$ !

伪代码/框架

浮点数二分(想想精度是多少)

$check()$:

  • 跑负权环板子(想想权值怎么算)
  • 注意:每个点都是起点

代码

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <cstring>
#include <cmath>
using namespace std;
const double EPS = 1e-4; // 精度问题

int n, m;
double f[1010]; // 浮点数
double dis[1010]; // 浮点数,千万别忘了
int vis[1010]; // 记录是否在队列里
int sum[1010]; // 入队次数

struct edge // 边
{
	int y, w;
} ;

vector<edge> g[1010]; // 图

void add(int x, int y, int w) // 建边
{
	g[x].push_back((edge){y, w});
}

bool check(double mid) // 检查
{
	queue<int> q;
	memset(dis, 0, sizeof(dis)); // 每个点都是起点
	memset(vis, 0, sizeof(vis));
	memset(sum, 0, sizeof(sum));
	for (int i = 1; i <= n; i++)
	{
		q.push(i); // 都要入队
		vis[i] = 1;
	}
	while (q.size())
	{
		int x = q.front();
		q.pop();
		vis[x] = 0;
		for (int i = 0; i < g[x].size(); i++)
		{
			int y = g[x][i].y;
			int w = g[x][i].w;
			if (dis[y] > dis[x] + mid * w - f[x]) // 权值
			{
				dis[y] = dis[x] + mid * w - f[x];
				if (vis[y] == 0)
				{
					q.push(y);
					vis[y] = 1;
					sum[y]++;
					if (sum[y] >= n)
						return true; // 存在负权值情况下的负权环(即正权环)
				}
			}
		}
	}
	return false; // 不存在
}

int main()
{
	cin >> n >> m;
	for (int i = 1; i <= n; i++)
		cin >> f[i];
	for (int i = 1; i <= m; i++)
	{
		int x, y, w;
		cin >> x >> y >> w;
		add(x, y, w);
	}
	double l = 0, r = 1000; // 浮点数二分
	while (r - l > EPS) // 二分
	{
		double mid = (l + r) / 2;
		if (check(mid))
			l = mid;
		else r = mid;
	}
	printf("%.2lf\n", l); // 输出
	return 0;
}

标签:int,题解,mid,double,Cows,include,1010,USACO07DEC,dis
From: https://www.cnblogs.com/thrift/p/18591509

相关文章

  • 题解:AT_abc371_c [ABC371C] Make Isomorphic
    题目大意有两个简单无向图,你每一次可以给第二个图添上或去掉一条边,有相应花费,问将两个图变为同构最少需要花费多少钱。思路观察数据范围,可以发现$N$非常小,可以考虑枚举全排列。所以我们就暴力枚举$1$到$N$,把这个当前排列记在一个数组里,$t[i]$表示在第一个图中点$i$对应......
  • 题解:AT_abc366_d [ABC366D] Cuboid Sum Query
    这是一个区间求和问题,因为Q很大,所以使用前缀和。N不超过100,所以在Q次询问中嵌套一次O(n)的循环是不会超时的。令s[i][j][k]表示第i层中左上角为(1,1),右下角为(j,k)的矩形中所有元素的和。s[i][j][k]=s[i][j-1][k]+s[i][j][k-1]-s[i][j-1][k-1]+a[i][j][k];然后在Q次询问中,枚举层数......
  • 题解:AT_abc369_e [ABC369E] Sightseeing Tour
    题目大意给定一个$N$个点,$M$条边的无向图。其中边有边权。有$Q$次询问,每一次给你$K$条必须经过的边(但是方向没有限制),问从$1$到$N$的最短路长度是多少。思路观察数据范围,可以发现:虽然$M$很大,但是$N$和$K$并不大。$K\le5$,可以暴力枚举每一条边经过时的方向以及......
  • 题解:CF1950F 0, 1, 2, Tree!
    题目链接思路不能形成树的情况:第一,一棵树必须有叶子节点。所以$c=0$的情况就一定不能形成一棵树。其次,可以发现,我们每增加一个度为$2$的节点,叶子节点就也会增加$1$个。所以$a+1\neqc$的情况也肯定不行了。代码片段if(!c||a+1!=c) cout<<"-1"<<endl;......
  • 题解:P4009 汽车加油行驶问题
    题目思路这是一个分层图最短路问题,我们可以使用升维的方法来完成本题。因为存在加油付费的问题,边权不一定为$1$,所以不能使用广搜来做。数据范围不大:$N\le100$。可以使用SPFA算法完成本题。每一个状态有三个值,分别是当前到达的行、列,以及剩下的油还能走几步。考虑是否需要加油......
  • 题解:P2217 [HAOI2007] 分割矩阵
    思路首先,我们要弄明白题中的方差是什么。公式:$S=\sqrt{\frac{1}{n}\sum_{i=1}^{n}(x_i-\bar{x})^2}$接下来,我们思考一下题目怎么做。数据很小,于是想到了暴搜。但是时间复杂度有点难以接受啊,优化一下吧。有一种很有效的优化,那就是广为人知的记忆化搜索。它能使所有......
  • 题解:AT_abc368_d[ABC368D] Minimum Steiner Tree
    题目大意题目给定一棵由$N$个节点组成的无根树,删除其中的一些点和边,使剩下的点和边仍然能够组成一棵树,且包含给定的$K$个特殊点,问最少剩下几个点。思路我们可以发现,这棵无根树的根必须是给定的特殊点之一,不然根节点就可以删除,答案就不是最优。所以我们使用深度优先搜索遍......
  • 题解:AT_abc368_c [ABC368C] Triple Attack
    思路$N$很大,导致$T$可能也会很大,所以一遍一遍的模拟就会超时。我们发现,题中有一个要求:每次必须打离自己最近的活着的敌人。我们就只用枚举每个敌人即可,在枚举的过程中计算答案。细节处理这道题有点难度,因为$T$是$3$的倍数时能量会变成$3$。这是个周期问题,自然会想到除......
  • CF2050G Tree Destruction 题解
    【题意简述】你有一棵树,你可以从里面删除一条链上的节点,问剩下的点的联通块数量最大是多少。【思路】一眼树形dp,默认根为\(1\)。我们以这棵树的\(1\)节点作为示例。设\(dp[i][0]\)表示\(i\)节点的子树中选一条链,\(i\)​不在链上的最大联通块数。设\(dp[i][1]\)......
  • CF2050F Maximum modulo equality 题解
    【题意简述】你有一个长度为\(n\)的数组\(a\)。每一次询问给定\(l,r\),寻找最大的\(m\)使得\(a_l\)到\(a_r\)的所有数对\(m\)同余,【前置数学芝士】首先是一个非常Naive的结论,请自行感性证明:设\(a>b\),\(a\)和\(b\)对\(a-b\)同余。理性证明:设\(p=a-b\),\(......