Forever Young

网络流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;
}
posted @ 2020-08-23 21:03  Loceaner  阅读(128)  评论(0编辑  收藏  举报