【知识点复习】负环与差分约束
前言
写博客写麻了。网课令人身心俱疲
参考链接:算法学习笔记(11): 差分约束
负环
负环:这还用解释?
最短路里面有提到过,这里就不赘述了(主要是懒啊呸摆)。
差分约束系统
定义:一种特殊的 元一次不等式组,包含 个变量, 个约束条件, 每个约束条件都是有两个变量作差构成的, 形如 。
对于差分约束系统而言,其中的每个约束条件都可以看做一条由点 连向点 的长为 的有向边。
注意到如果 { } 为一组解,
那么对于任意常数 ,{ } 也是一组解。
所以,不妨先求一组负数解,然后再增加一个 号节点, 令 。
这样一来,就多增加了 条从节点 连向节点 的有向边。
令 ,套进求单源最短路, 即为一组解。
另外,如果图中存在负环,说明该差分约束系统无解。
应用
观光奶牛
给定一张 L 个点、P 条边的有向图,每个点都有一个权值 f[i],每条边都有一个权值 t[i]。
求图中的一个环,使“环上各点的权值之和”除以“环上各边的权值之和”最大。
输出这个最大值。
注意:数据保证至少存在一个环。
同样是二分答案。
因为题目保证至少存在一个环,所以不用考虑成环的问题。
因此“环上各点的权值之和 除以 环上各边的权值之和” 可以暂时理解成 “单个点的边权 除以 点权”, 下文记作 。
检验时,通过二分的 求出理想边权(非实际),然后建立差分约束系统,通过记录每个点的点权改变次数判断是否存在负环 ,如果存在负环,说明差分约束无解,答案应该缩小;否则答案有增大的可能。
点击查看代码
#include<cstdio>
#include<algorithm>
using namespace std;
const double eps = 1e-4;
const int N = 1005, M = 5005;
int n, m, q[N * M], a[N], cnt[N];
int tot, head[N], nex[M], to[M], w[M];
bool vis[N];
double dis[N];
void add(int x, int y, int z) {
to[++tot] = y, nex[tot] = head[x], head[x] = tot, w[tot] = z;
}
bool check(double mid) {
int now, ver, st = 0, ed = -1;
double tmp;
for(int i = 1; i <= n; i ++) vis[i] = 1, dis[i] = cnt[i] = 0, q[++ed] = i;
while(st <= ed) {
now = q[st ++], vis[now] = 0;
for(int i = head[now]; i; i = nex[i]) {
ver = to[i], tmp = w[i] * mid - a[now];
if(dis[ver] > dis[now] + tmp) {
dis[ver] = dis[now] + tmp;
cnt[ver] = cnt[now] + 1;
if(cnt[ver] >= n) return 1;
if(!vis[ver]) q[++ed] = ver;
}
}
}
return 0;
}
int main() {
scanf("%d %d", &n, &m);
for(int i = 1; i <= n; i ++) scanf("%d", &a[i]);
int u, v, z;
while(m --) {
scanf("%d %d %d", &u, &v, &z);
add(u, v, z);
}
double l = 0, r = 1000, mid;
while(r - l > eps) {
mid = (l + r) / 2;
if(check(mid)) l = mid;
else r = mid;
}
printf("%.2lf", r);
return 0;
}
区间
给定 n 个区间 [ai,bi] 和 n 个整数 ci 。
你需要构造一个整数集合 Z,使得 ∀i∈[1,n],Z 中满足 ai≤x≤bi 的整数 x 不少于 ci 个。
求这样的整数集合 Z 最少包含多少个数。
就是个板题,看题可以找的非常明显的条件 ,直接连边就好。
但是题目还有一些需要注意的其他约束条件,才能保证求出的解有意义:
-
—— 之间选出的数肯定大于 ;
-
—— 每个数只能选一次;
-
—— 显然。
这样建立的差分约束系统才算完整。
记图中的最大标号为 。
跑一遍 , 最后 即为所求答案。
点击查看代码
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<queue>
using namespace std;
const int N = 5e4 + 5;
int n, tot, mx, head[N], nex[N * 5], to[N *5], w[N *5];
void add(int x, int y, int z) {
to[++tot] = y, nex[tot] = head[x], w[tot] = z, head[x] = tot;
}
int dis[N];
queue<int> q;
bool vis[N];
void Spfa(int s) {
memset(dis, -0x3f, sizeof(dis));
dis[s] = 0, vis[s] = 1, q.push(s);
int now, ver;
while(!q.empty()) {
now = q.front(), q.pop(), vis[now] = 0;
for(int i = head[now]; ~i; i = nex[i]) {
ver = to[i];
if(dis[ver] < dis[now] + w[i]) {
dis[ver] = dis[now] + w[i];
if(!vis[ver]) {
vis[ver] = 1;
q.push(ver);
}
}
}
}
}
int main() {
scanf("%d", &n);
int a, b, c;
memset(head, -1, sizeof(head));
for(int i = 1; i <= n; i ++) {
scanf("%d %d %d", &a, &b, &c), b ++;
mx = max(max(a, b), mx);
add(0, b, 0), add(0, a, 0), add(a, b, c);
}
for(int i = 1; i <= mx; i ++) add(i - 1, i, 0), add(i, i - 1, -1);
Spfa(0);
printf("%d", dis[mx]);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现