[AHOI2014/JSOI2014]支线剧情
题目大意
有一个图,然后每条边有费用。
然后你要选择一些从 1 出发的路径使得每条边至少被经过一次。
求最小费用。
思路
考虑如何网络流建图,然后你发现路径就是流量,这个至少经过一次其实可以看做是一个上下界(上界 inf 下界为 1).
然后每条边的费用的话其实就是最小费用。
所以其实就是有源汇点上下界最小费用可行流。
那接着你考虑怎么做。
然后你会发现,其实你好像就在有源汇上下界可行流的基础上,把你原图的边加上费用,然后跑的是费用流即可。
代码
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
const int N = 300 + 10;
const int M = 50 + 10;
struct node {
int x, val, to, nxt, op;
}e[N * M * 2 + N + N];
int n, m, S, T, le[N + 10], tot, KK, disum;
int ru[N + 10], chu[N + 10], s1, s2, t1, t2;
int dis[N + 10], lee[N + 10], deg[N + 10];
bool in[N + 10];
void Add(int x, int y, int z, int w) {
e[++KK] = (node){z, w, y, le[x], KK + 1}; le[x] = KK;
e[++KK] = (node){0, -w, x, le[y], KK - 1}; le[y] = KK;
}
bool SPFA() {
memset(deg, 0x7f, sizeof(deg));
memset(dis, 0x7f, sizeof(dis));
memcpy(lee, le, sizeof(lee));
deg[S] = 0; dis[S] = 0;
queue <int> q; q.push(S); in[S] = 1;
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[now] + e[i].val) {
dis[e[i].to] = dis[now] + e[i].val; deg[e[i].to] = deg[now] + 1;
if (!in[e[i].to]) {
in[e[i].to] = 1; q.push(e[i].to);
}
}
in[now] = 0;
}
return dis[T] != dis[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 && deg[e[i].to] == deg[now] + 1 && dis[e[i].to] == dis[now] + e[i].val) {
int this_go = dfs(e[i].to, min(sum - go, e[i].x));
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 (SPFA())
re += dfs(S, INF) * dis[T];
return re;
}
int main() {
scanf("%d", &n);
tot = n; s1 = 1; t1 = ++tot; s2 = ++tot; t2 = ++tot;
for (int i = 1; i <= n; i++) {
int k, b, t; scanf("%d", &k);
for (int j = 1; j <= k; j++) {
scanf("%d %d", &b, &t);
Add(i, b, INF, t);
chu[i] += 1; ru[b] += 1;
disum += 1 * t;
}
}
for (int i = 2; i <= n; i++) {
Add(i, t1, INF, 0);
}
for (int i = 1; i <= n; i++) {
if (ru[i] > chu[i]) Add(s2, i, ru[i] - chu[i], 0);
if (chu[i] > ru[i]) Add(i, t2, chu[i] - ru[i], 0);
}
Add(t1, s1, INF, 0);
S = s2; T = t2;
printf("%d", disum + Dinic());
return 0;
}
__EOF__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下