kyeremal-网络流24题T4-魔术球问题

13 篇文章 0 订阅

原题:04-魔术球问题


分析:直接求解较难,所以考虑使用二分/枚举答案+验证的方法,二分或枚举出一个答案后,即枚举出数字的集合:A=[1,N],那么对于每一组i,j∈A,如果i<j,且i+j为完全平方数,那么建立一条从i->j的有向边,那么问题就转化为求这个图的最小点路径覆盖。


code:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>
 
using namespace std;
 
#define rep(i, l, r) for (int i = l; i <= r; i++)
#define REP(i, l, r) for (int i = l; i >= r; i--)
#define EPS (1e-7)
#define INF 19971228
#define MAXN 1000010
 
int n, N, first[MAXN], next[MAXN], S, T, dis[MAXN];
struct tlist {int x, y, f;} a[MAXN];
queue<int> q;
 
inline int min(int a, int b) {return a<b ? a : b;}
inline int max(int a, int b) {return a>b ? a : b;}
inline double absD(double a, double b) {return (a > b) ? a-b : b-a;}
inline bool square(int n) {return absD(sqrt(n*1.0), int(sqrt(n))) < EPS;}
 
inline void add(int x, int y, int f) {
    a[++N].x = x, a[N].y = y, a[N].f = f, next[N] = first[x], first[x] = N;
    a[++N].x = y, a[N].y = x, a[N].f = 0, next[N] = first[y], first[y] = N;
}

inline void init() {
    N = -1;
    memset(next, -1, sizeof(next));
    memset(first, -1, sizeof(first));
}
 
inline bool bfs() {
    while (!q.empty()) q.pop();
    memset(dis, -1, sizeof(dis));
    q.push(S);
    dis[S] = 0;
    while (!q.empty()) {
        int x = q.front();
        q.pop();
        for (int i = first[x]; ~i; i = next[i])
            if (!~dis[a[i].y] && a[i].f) {
                dis[a[i].y] = dis[x] + 1;
                q.push(a[i].y);
            }
    }
    return ~dis[T];
}
 
inline int find(int x, int low) {
    int sum = 0, temp;
    if (x == T) return low;
    for (int i = first[x]; ~i; i = next[i])
        if ((a[i].f) && (dis[a[i].y] == dis[x] + 1) && (temp = find(a[i].y, min(low-sum, a[i].f)))) {
            a[i].f -= temp;
            a[i^1].f += temp;
            sum += temp;
        }
    return sum;
}
 
inline int dinic(int begin, int end) {
    S = begin, T = end;
    int temp, ans = 0;
    while (bfs())
        while (temp = find(S, 0x7fffffff))
            ans += temp;
    return ans;
}
 
inline int make(int K) {
	init();
    rep(i, 1, K) add(0, i, 1);
    rep(i, 1, K) add(K+i, 2*K + 1, 1);
    rep(i, 1, K) rep(j, i+1, K)
        if (square(i+j)) add(i, K+j, INF);
    return K - dinic(0, 2*K + 1);
}
 
int main() {
   // freopen("zyq.in", "r", stdin);
    cin >> n;
    int L = 1, R = 3000, mid;
    while ((R-L) > 1) {
        mid = (L + R) >> 1;
        if (make(mid) <= n) L = mid;
        else R = mid;
    }
    int out = 0;
    if (make(R) <= n) out = R;
    out = max(L, out);
    cout << out << endl;

    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值