[题解] PowerOJ 1739 魔术球问题 (最大流)
- 传送门 -
https://www.oj.swust.edu.cn/problem/show/1739
Time Limit: 1000 MS Memory Limit: 65536 KB
Total Submit: 422 Accepted: 184 Page View: 1330
Description
假设有n根柱子,现要按下述规则在这n根柱子中依次放入编号为1,2,3,...的球。 (1)每次只能在某根柱子的最上面放球。 (2)在同一根柱子中,任何2个相邻球的编号之和为完全平方数。 试设计一个算法,计算出在n根柱子上最多能放多少个球。例如,在4 根柱子上最多可 放11 个球。 编程任务: 对于给定的n,计算在n根柱子上最多能放多少个球。
Input
由文件input.txt提供输入数据。文件第1 行有1个正整数n(n<=55),表示柱子数。
Output
程序运行结束时,将n 根柱子上最多能放的球数以及相应的放置方案输出到文件 output.txt中。文件的第一行是球数。接下来的n行,每行是一根柱子上的球的编号。
4
11 1 8 2 7 9 3 6 10 4 5 11
Source
- 思路 -
若 i < j 且 i + j 为完全平方数, 则从起点 i 向终点 j 连一条边, 这个问题和最小路径覆盖问题是一样的.
但是起点终点要怎么分别表示呢, 球的个数是未知的, 我先写了个暴搜搜索 n = 55 的情况, 大概在1300,1400的样子, 于是开了2000, 起点为 x, 终点为 2000 + x.
输出方案真的是卡成狗哦!
细节见代码.
- 代码 -
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
const int N = 4000 + 5;
const int M = 1e6;
const int TMP = 2000;
const int inf = 0x3f3f3f3f;
int ANS[N<<1];
int NXT[M], TO[M], FRM[M];
int V[M], HD[N], GO[N];
int CUR[N], DIS[N], VIS[N];
int n, m, sz, cnt, ans, ss, tt;
queue<int> q;
void add(int x, int y) {
TO[sz] = y; NXT[sz] = HD[x]; HD[x] = sz; FRM[sz] = x; V[sz++] = 1;
TO[sz] = x; NXT[sz] = HD[y]; HD[y] = sz; FRM[sz] = y; V[sz++] = 0;
}
void init(int x) {
for (int i = 0; i < sz; ++i)
V[i] = (i%2) ^ 1;
add(ss, x);
add(x + TMP, tt);
for (int i = 1; i < x; ++i) {
if (sqrt(i+x) - int(sqrt(i+x)) > 0) continue;
else add(i, x + TMP);
}
}
bool bfs() {
memset(DIS, -1, sizeof (DIS));
DIS[ss] = 0;
q.push(ss);
while (!q.empty()) {
int u = q.front();
q.pop();
for (int i = HD[u]; i != -1; i = NXT[i]) {
int v = TO[i];
if (DIS[v] < 0 && V[i]) {
DIS[v] = DIS[u] + 1;
q.push(v);
}
}
}
return DIS[tt] > 0;
}
int dfs(int x, int a) {
if (x == tt) return a;
int flow = 0, f;
for (int& i = CUR[x]; i != -1; i = NXT[i]) {
if (V[i] && DIS[TO[i]] == DIS[x] + 1)
if (f = dfs(TO[i], min(a, V[i]))) {
V[i] -= f;
V[i^1] += f;
flow += f;
a -= f;
if (a == 0) break;
}
}
return flow;
}
int dinic(int x) {
int flow = 0;
while (bfs()) {
memcpy(CUR, HD, sizeof (HD));
flow += dfs(ss, inf);
}
return (x - flow);
}
void print(int x) {
for (int i = 0; i < sz; ++i) {
if (V[i] == 0 && TO[i] > FRM[i] && FRM[i] != ss && FRM[i] != tt
&& TO[i] != ss && TO[i] != tt)
GO[FRM[i]] = TO[i] - TMP;
}
memset(VIS, 0, sizeof (VIS));
int i = 1, j;
cnt = 0;
while (i <= x) {
if (VIS[i]) {
i++;
continue;
}
ANS[++cnt] = i;
VIS[i] = 1;
j = i;
while (GO[j]) {
j = GO[j];
VIS[j] = 1;
ANS[++cnt] = j;
}
ANS[++cnt] = -1;
}
}
int main() {
scanf("%d", &n);
ss = 0, tt = 4000;
memset(HD, -1, sizeof (HD));
for (int i = 1; ; ++i) {
init(i);
int tp = dinic(i);
if (tp > n) {
printf("%d\n", i - 1);
for (int j = 1; j <= cnt; ++j) {
if (ANS[j] == -1) printf("\n");
else printf("%d ", ANS[j]);
}
break;
}
if (tp == n) print(i); //开始记录方案
}
return 0;
}