给你一个图,边是双向且两边走的费用不同。 要你选一些边来走,然后使得形成一条欧拉回路,且所选边的最大费用最小。 要输出这个费用和选的边。
欧拉回路 / 桥
题目大意
给你一个图,边是双向且两边走的费用不同。
要你选一些边来走,然后使得形成一条欧拉回路,且所选边的最大费用最小。
要输出这个费用和选的边。
思路
首先这个最大费用最小直接用二分。
那么接着就是判断了,我们可以把边分成两类:
只能有一个方向的和两个方向都可以的。(如果有两边的都走不了肯定不行,因为你欧拉回路要经过所有边)
那只有一个方向的就直接确定了,两个方向的就不确定。
先看如果判断是否形成欧拉回路,即要每个点的入度和出度相同。
如果你考虑用网络流来选择两个方向的,你会发现它会出现流了一半的问题。
所以就有一个小方法:你先随便选一个方向,然后如果选回去那就是一个点入度减 2,出度加 2,另一个点反过来。
那这样每次要么 0 要么 2,我们可以集体把流量都 /2 变成 0,1,这样就不会出现流一半了。
具体来讲,你选边先,然后每个点有一个 Δ 就是入度和出度的差,如果 Δ>0 就从源点向它连流量为 Δ2,如果 <0 就连到汇点流量为 −Δ2。
(如果 Δ 是奇数就肯定不行但是不难看出肯定是偶数)
然后至于双向都可以走的边 (x,y),一开始用的是 x→y,那就从 y 到 x 连一条边流量为 1。
然后判断既是看这些从源点汇点连的边是否都流满了。
然后注意判 NIE,就是如果不连通是 NIE,原本的无向图都没有欧拉回路是 NIE。
代码
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
struct node {
int x, y, v1, v2;
}a[2001];
int n, m, L, R, ans;
int ru[2001], chu[2001];
int fa[2001], S, T, tot;
int dy[2001];
vector <int> b[2001], goo[2001], pl[2001], answ;
struct water {
int x, to, nxt, op, fr;
}e[2000001];
int le[2001], KK, lee[2001];
int find(int now) {
if (fa[now] == now) return now;
return fa[now] = find(fa[now]);
}
void merge(int x, int y) {
int X = find(x), Y = find(y);
if (X == Y) return ;
fa[X] = Y;
}
void Add(int x, int y, int z, int k) {
e[++KK] = (water){z, y, le[x], KK + 1, k}; le[x] = KK;
e[++KK] = (water){0, x, le[y], KK - 1, k}; le[y] = KK;
}
queue <int> q;
int dis[2001];
bool bfs() {
while (!q.empty()) q.pop();
memset(dis, 0, sizeof(dis));
for (int i = 1; i <= tot; i++) lee[i] = le[i];
dis[S] = 1; q.push(S);
while (!q.empty()) {
int now = q.front(); q.pop();
for (int i = le[now]; i; i = e[i].nxt)
if (e[i].x && !dis[e[i].to]) {
dis[e[i].to] = dis[now] + 1;
if (e[i].to == T) return 1;
q.push(e[i].to);
}
}
return 0;
}
int dfs(int now, int sum) {
if (now == T) return sum;
int go = 0;
for (int &i = lee[now]; i; i = e[i].nxt)
if (e[i].x && dis[e[i].to] == dis[now] + 1) {
int this_go = dfs(e[i].to, min(e[i].x, sum - go));
if (this_go) {
e[i].x -= this_go; e[e[i].op].x += this_go;
go += this_go; if (go == sum) return go;
}
}
if (go != sum) dis[now] = -1;
return go;
}
int Dinic() {
int re = 0;
while (bfs())
re += dfs(S, INF);
return re;
}
bool check(int k) {
KK = 0; memset(le, 0, sizeof(le));
tot = n; S = ++tot; T = ++tot;
memset(ru, 0, sizeof(ru)); memset(chu, 0, sizeof(chu));
for (int i = 1; i <= m; i++) {
if (a[i].v1 <= k && a[i].v2 <= k) {
chu[a[i].x]++; ru[a[i].y]++;
Add(a[i].y, a[i].x, 1, i);
}
else if (a[i].v1 <= k) {
chu[a[i].x]++; ru[a[i].y]++;
}
else {
chu[a[i].y]++; ru[a[i].x]++;
}
}
int sum = 0;
for (int i = 1; i <= n; i++) {
int derta = ru[i] - chu[i];
if (derta > 0) Add(S, i, derta / 2, 0), sum += derta / 2;
else if (derta < 0) Add(i, T, -derta / 2, 0);
}
int nowsum = Dinic();
return nowsum == sum;
}
int num;
bool in[2001];
void Runway(int now) {
for (int i = 0; i < b[now].size(); i++) {
if (goo[now][i]) continue;
else {
goo[now][i] = 1;
Runway(b[now][i]);
answ.push_back(pl[now][i]);
}
}
}
int main() {
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++) fa[i] = i;
for (int i = 1; i <= m; i++) {
scanf("%d %d %d %d", &a[i].x, &a[i].y, &a[i].v1, &a[i].v2);
L = max(L, min(a[i].v1, a[i].v2));
R = max(R, max(a[i].v1, a[i].v2));
ru[a[i].x]++; ru[a[i].y]++;
merge(a[i].x, a[i].y);
}
for (int i = 1; i <= n; i++)
if (find(i) != find(1)) {
printf("NIE"); return 0;
}
for (int i = 1; i <= n; i++)
if (ru[i] & 1) {
printf("NIE"); return 0;
}
else ru[i] = 0;
while (L <= R) {
int mid = (L + R) >> 1;
if (check(mid)) ans = mid, R = mid - 1;
else L = mid + 1;
}
printf("%d\n", ans);
check(ans);
for (int i = 1; i <= m; i++) {
if (a[i].v1 <= ans && a[i].v2 <= ans) {
}
else if (a[i].v1 <= ans) {
b[a[i].x].push_back(a[i].y); goo[a[i].x].push_back(0);
pl[a[i].x].push_back(i);
}
else {
b[a[i].y].push_back(a[i].x); goo[a[i].y].push_back(0);
pl[a[i].y].push_back(i);
}
}
for (int x = 1; x <= n; x++) {
for (int i = le[x]; i; i = e[i].nxt)
if (e[i].fr && !e[i].x) {
int y = e[i].to;
b[x].push_back(y); goo[x].push_back(0);
pl[x].push_back(e[i].fr);
}
}
Runway(1);
for (int i = answ.size() - 1; i >= 0; i--)
printf("%d ", answ[i]);
return 0;
}
__EOF__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
2021-02-08 GDKOI2021 爆炸记