[题解] PowerOJ 1739 魔术球问题 (最大流)

- 传送门 -

 https://www.oj.swust.edu.cn/problem/show/1739

#  1739: 魔术球问题 SPJ

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

线性规划与网络流24题

 

- 思路 -

 若 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;
}
posted @ 2017-08-22 16:04  lstttt  阅读(176)  评论(0编辑  收藏  举报