原题: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;
}