网络流24题 P2765 魔术球问题
思路
问题模型: 有向无环图最小路径覆盖
转化模型:网络最大流
看了好久不知道跟网络流有什么关系……
与最小路径覆盖问题这道题类似, 从小到大枚举球数,将球拆成两点,将其与比自己小的平方数的差连边,进而保证每次只和比自己小的数连边,这样就不会重边。
每次求最大流,不需修改上次的边,因为残留网络可以直接拿来用,如果某次需要的柱子数大于 \(n\) 了,就跳出,答案就是 当前球的数量 \(-1\)
记录路径可以在求最大流过程中完成,细节见代码
代码
/*
Name: 魔术球问题
Author: Loceaner
Date: 23/08/20 17:39
Description: 求最小点覆盖
Debug: 记得加当前弧优化,否则会跑不过
注意数组的下标会不会减出负数,如果有负数会RE
*/
#include <cmath>
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int A = 2e6 + 11;
const int B = 1e6 + 11;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
inline int read() {
char c = getchar();
int x = 0, f = 1;
for ( ; !isdigit(c); c = getchar()) if (c == '-') f = -1;
for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
return x * f;
}
int n, m, s, t, cnt = 1, sum, jump;
struct node { int to, nxt, val; } e[A];
int net[A], sq[A], head[A], vis[A], cur[A], dep[A], in[A]; //square平方
inline void add(int from, int to, int val) {
e[++cnt].to = to;
e[cnt].val = val;
e[cnt].nxt = head[from];
head[from] = cnt;
}
inline bool bfs() {
queue <int> Q;
for (int i = 1; i <= 50001; i++)
dep[i] = inf, vis[i] = 0, cur[i] = head[i];
Q.push(s), dep[s] = 0, vis[s] = 1;
while (!Q.empty()) {
int x = Q.front();
Q.pop(), vis[x] = 0;
for (int i = head[x]; i; i = e[i].nxt) {
int to = e[i].to;
if (dep[to] > dep[x] + 1 && e[i].val) {
dep[to] = dep[x] + 1;
if (!vis[to]) vis[to] = 1, Q.push(to);
}
}
}
if (dep[t] != inf) return 1;
return 0;
}
int dfs(int x, int flow) {
int tmp = 0;
if (x == t) return flow;
for (int i = cur[x]; i; i = e[i].nxt) {
cur[x] = i;
int to = e[i].to;
if (dep[to] == dep[x] + 1 && e[i].val) {
if (tmp = dfs(to, min(flow, e[i].val))) {
e[i].val -= tmp, e[i ^ 1].val += tmp;
net[x] = to;
if (x != t && x != 0) if (to - jump > 0) in[to - jump] = 1;
return tmp;
}
}
}
return 0;
}
int main() {
n = read(), s = 50000, t = 50001;
for (int i = 1; i <= 1000; i++) sq[i] = i * i;
int tot = 1;
jump = 10000;
while (1) {
add(s, tot, 1), add(tot, s, 0);
add(tot + jump, t, 1), add(t, tot + jump, 1);
int square = lower_bound(sq + 1, sq + 1 + 1000, tot) - sq;
for (int i = square * 2; i >= 1; i--) {
int now = sq[i] - tot;
if (now < tot && now > 0)
add(now, tot + jump, 1), add(tot + jump, now, 0);
}
int rec = 0, ansnow = 0;
while (bfs()) while (rec = dfs(s, inf)) ansnow += rec;
sum += ansnow;
if (tot - sum > n) break;
//所需的柱子数量>n则弹出,那么tot-1就是答案
tot++;
}
cout << --tot << '\n';
for (int i = 1; i <= tot; i++) {
if (!in[i]) { //起点
int cur = i;
cout << cur << " ";
while (net[cur] != t && net[cur] != 0) {
cout << net[cur] - jump << " ";
cur = net[cur] - jump; //连接的下一个点
}
puts("");
}
}
return 0;
}
转载不必联系作者,但请声明出处