网络流与线性规划24题做题笔记

感觉不知道做啥了 练练网络流

汽车加油行驶问题

luogu link

绝大多数人的第一道24题 因为是披着网络流皮的最短路

建分层图 第 \(k\) 层表示当前剩余油量为 \(k\)
往右/下移动费用为 \(0\) 剩下两个方向费用为 \(B\)
对于没有加油站的点 往第 \(K\) 层同一位置连边 费用为 \(A + C\)
对于有加油站的点 所有不在第 \(K\) 层的点不往四个方向连边 只向第 \(K\) 层连一条费用为 \(A\) 的边
对起点和终点建电梯 费用为 \(0\)
所有边的流量均为 \(1\) 跑最小费用最大流即可
发现最大流一定为 \(1\) 所以实际上就是最短路

点击查看代码
#include <bits/stdc++.h>
#define ll long long
#define int long long
using namespace std;

inline int read() {
	int xr = 0, F = 1;
	char cr;
	while (cr = getchar(), cr < '0' || cr > '9') if (cr == '-') F = -1;
	while (cr >= '0' && cr <= '9')
		xr = (xr << 1) + (xr << 3) + (cr ^ 48), cr = getchar();
	return xr * F;
}

namespace steven24 {
	
const int N = 2e6 + 0721;
const int inf = 0x7fffffff;
int mp[105][105];
int dx[4] = {1, -1, 0, 0};
int dy[4] = {0, 0, 1, -1};	
int n, k, A, B, C;

struct network_flows {
	
int minc[N], incf[N], pre[N];
int head[N], f[N], cost[N], to[N], nxt[N];
int cnt = 1;
int maxflow, mincost;
int S, T;

inline void add_edge(int x, int y, int c, int flow) {
	to[++cnt] = y;
	nxt[cnt] = head[x];
	head[x] = cnt;
	f[cnt] = flow;
	cost[cnt] = c;
	
	to[++cnt] = x;
	nxt[cnt] = head[y];
	head[y] = cnt;
	f[cnt] = 0;
	cost[cnt] = -c;
}

bool exist[N];
queue<int> q;
bool spfa() {
	while (!q.empty()) q.pop();
	for (int i = 0; i <= n * n * (k + 1) + 1; ++i) {
		minc[i] = inf;
		exist[i] = 0;
	}
	
	exist[S] = 1;
	minc[S] = 0;
	incf[S] = inf;
	q.push(S);
	
	
	while (!q.empty()) {
		int now = q.front();
		q.pop();
		exist[now] = 0;
		
		for (int i = head[now]; i; i = nxt[i]) {
			if (!f[i]) continue;
			int y = to[i];
			if (minc[y] > minc[now] + cost[i]) {
				minc[y] = minc[now] + cost[i];
				pre[y] = i;
				incf[y] = min(incf[now], f[i]);
				if (!exist[y]) exist[y] = 1, q.push(y);
			}
		}
	}
	if (minc[T] == inf) return 0;
	else return 1;
}

void MCMF() {
	while (spfa()) {
		int x = T;
		maxflow += incf[T];
		mincost += incf[T] * minc[T];
		int i;
		while (x != S) {
			i = pre[x];
			f[i] -= incf[T];
			f[i ^ 1] += incf[T];
			x = to[i ^ 1];
		}
	}
}
	
} G;

inline int change(int x, int y) {
	return (x - 1) * n + y;
}

bool exist(int x, int y) {
	return x >= 1 && x <= n && y >= 1 && y <= n;
}

void main() {
	n = read(), k = read(), A = read(), B = read(), C = read();
	for (int i = 1; i <= n; ++i) for (int j = 1; j <= n; ++j) mp[i][j] = read();
	G.S = 1 + (k + 1) * n * n;
	G.add_edge(G.S, change(1, 1) + k * n * n, 0, 1);
	G.T = 0;
	G.add_edge(change(n, n), G.T, 0, 1);
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= n; ++j) {
			int id = change(i, j);
			if (mp[i][j]) {
				for (int s = 0; s < k; ++s) G.add_edge(id + n * n * s, id + n * n * k, A, 1);
				for (int s = 0; s < 4; ++s) {
					int x = i + dx[s], y = j + dy[s];
					if (!exist(x, y)) continue;
					if (dx[s] < 0 || dy[s] < 0) G.add_edge(id + n * n * k, change(x, y) + (k - 1) * n * n, B, 1);
					else G.add_edge(id + n * n * k, change(x, y) + (k - 1) * n * n, 0, 1);
				} 
			} else {
				for (int s = 1; s <= k; ++s) {
					for (int p = 0; p < 4; ++p) {
						int x = i + dx[p], y = j + dy[p];
						if (!exist(x, y)) continue;
						if (dx[p] < 0 || dy[p] < 0) G.add_edge(id + n * n * s, change(x, y) + (s - 1) * n * n, B, 1);
						else G.add_edge(id + n * n * s, change(x, y) + (s - 1) * n * n, 0, 1);
					}
				}
				for (int s = 0; s < k; ++s) G.add_edge(id + n * n * s, id + n * n * k, A + C, 1);
			}
		}
	}
	for (int i = 1; i <= k; ++i) G.add_edge(change(n, n) + n * n * i, change(n, n), 0, 1);
	G.MCMF();
	printf("%lld\n", G.mincost);
}

}

signed main() {
	steven24::main();
	return 0;
}

圆桌问题

luogu link

见代码开头部分

点击查看代码
/*
起点往每个单位连流量为r的边
每个单位往每个桌连流量为1的边(桌 = id + m)
每个桌往终点连流量为c的边(T = n + m + 1)
跑最大流
*/
#include <bits/stdc++.h>
#define ll long long
using namespace std;

inline int read() {
	int xr = 0, F = 1;
	char cr;
	while (cr = getchar(), cr < '0' || cr > '9') if (cr == '-') F = -1;
	while (cr >= '0' && cr <= '9')
		xr = (xr << 1) + (xr << 3) + (cr ^ 48), cr = getchar();
	return xr * F;
}

namespace steven24 {
	
const int N = 521;
const int M = 1e5 + 0721;
const int inf = 0x7fffffff;
int head[N], nxt[M], to[M], fl[M], cnt = 1;
int dis[N], cur[N];
int r[N], cc[N];
int n, m, S, T;
vector<int> ans[N];

inline void add_edge(int x, int y, int z) {
	to[++cnt] = y;
	nxt[cnt] = head[x];
	head[x] = cnt;
	fl[cnt] = z;
	
	to[++cnt] = x;
	nxt[cnt] = y;
	head[y] = cnt;
	fl[cnt] = z;
}

bool bfs() {
	queue<int> q;
	for (int i = 0; i <= n + m + 1; ++i) dis[i] = -1;
	for (int i = 0; i <= n + m + 1; ++i) cur[i] = head[i];
	dis[S] = 0;
	q.push(S);
	while (!q.empty()) {
		int now = q.front();
		q.pop();
		if (now == T) return 1;
		for (int i = head[now]; i; i = nxt[i]) {
			int y = to[i];
			if (fl[i] > 0 && dis[y] == -1) {
				dis[y] = dis[now] + 1;
				q.push(y);
			}
		}
	}
	return 0;
} 

int dinic(int x, int res) {
	if (!res || x == T) return res;
	int flow = 0, c;
	for (int i = cur[x]; i && res > 0; i = nxt[i]) {
		cur[x] = i;
		int y = to[i];
		if (fl[i] > 0 && dis[y] == dis[x] + 1) {
			c = dinic(y, min(res, fl[i]));
			if (!c) dis[y] = -1;
			fl[i] -= c;
			fl[i ^ 1] += c;
			res -= c;
			flow += c;
		}
	}
	return flow;
}

void build_map() {
	S = 0, T = n + m + 1;
	for (int i = 1; i <= m; ++i) add_edge(S, i, r[i]);
	for (int i = 1; i <= m; ++i) {
		for (int j = 1; j <= n; ++j) add_edge(i, j + m, 1);
	}
	for (int j = 1; j <= n; ++j) add_edge(j + m, T, cc[j]);
}

void main() {
	m = read(), n = read();
	int sum = 0;
	for (int i = 1; i <= m; ++i) {
		r[i] = read();
		sum += r[i];
	}
	for (int i = 1; i <= n; ++i) cc[i] = read();
	build_map();
	
	int maxflow = 0;
	while (bfs()) maxflow += dinic(S, inf);
	
	if (maxflow < sum) puts("0");
	else {
		puts("1");
		for (int i = 1; i <= m; ++i) {
			for (int j = head[i]; j; j = nxt[j]) {
				int y = to[j] - m;
//				cout << i << " " << y << " " << fl[j] << "\n";
				if (!fl[j] && y > 0) printf("%d ", y);
			}
			printf("\n");
		}
	}

}
	
}

int main() {
	steven24::main();
	return 0;
}
/*
4 5
4 5 3 5
3 5 2 6 4
*/
posted @ 2023-11-02 19:07  Steven24  阅读(31)  评论(1)    收藏  举报