线性规划与网络流24题--魔术球问题

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

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行,每行是一根柱子上的球的编号。

Sample Input

4

Sample Output

11
1 8
2 7 9
3 6 10
4 5 11

这个题就是直接枚举球的个数,在符合条件的两个球之前建一条边(小指向大),然后网络流求出最小路径覆盖,当最小路径覆盖大于n时,比当前球个数少一就是答案,然后就是输出路径。
ps:如果向已经跑完dinic的图再加边或者修改边,再次跑出来的dinic就是新增加的流
代码:

//
#include<bits/stdc++.h>
using namespace std;
const int maxn=5000;
const int inf=0x3f3f3f3f;
struct node{
    int v,f,next;
}E[maxn*10];
int n,s,t,tot,maxm,ans;
int head[maxn],depth[maxn],cur[maxn];
void init()
{
    tot=-1;
    memset(head,-1,sizeof head);
}
void add_edge(int u,int v,int w)
{
    tot++;
    E[tot]={v,w,head[u]};
    head[u]=tot;
}
bool bfs()
{
    queue<int>q;
    memset(depth, 0, sizeof depth);
    depth[s] = 1;
    q.push(s);
    while (!q.empty()) {
        int u = q.front();
        q.pop();
        for (int i = head[u]; ~i; i = E[i].next) {
            if (E[i].f > 0 && depth[E[i].v] == 0) {
                depth[E[i].v] = depth[u] + 1;
                q.push(E[i].v);
            }
        }
    }
    return (bool)depth[t];
}
int dfs(int u, int flow) {
    if (u == t) return flow;
    for (int& i = cur[u]; ~i; i = E[i].next) {
        if (depth[E[i].v] == depth[u] + 1 && E[i].f > 0) {
            int temp = dfs(E[i].v, min(flow, E[i].f));
            if (temp > 0) {
                E[i].f -= temp;
                E[i ^ 1].f += temp;
                return temp;
            }
        }
    }
    return 0;
}
int dinic() {
    int ans = 0;
    while (bfs()) {
        int temp;
        for(int i=0;i<maxn;i++) cur[i]=head[i];
        while (temp = dfs(s, inf)) {
            ans += temp;
        }
    }
    return ans;
}
inline bool check(int x)
{
    return (int(sqrt(x))*int(sqrt(x)))==x;
}
void print(int u)
{
    for(int i=head[u];~i;i=E[i].next){
        if(E[i].f==0&&E[i].v-maxm!=ans+1){
            if(E[i].v==s){
                puts("");
                return;
            }
            printf("%d ",E[i].v-maxm);
            print(E[i].v-maxm);
        }
    }
}
int main()
{
    scanf("%d",&n);
    s=0,t=maxn-1,maxm=maxn/2;
    init();
    for(int i=1,temp=1;;i++,temp++){
        for(int j=1;j<i;j++){
            if(check(i+j)){
                add_edge(j,i+maxm,1);
                add_edge(maxm+i,j,0);
            }
        }
        add_edge(s,i,1);
        add_edge(i,s,0);
        add_edge(i+maxm,t,1);
        add_edge(t,i+maxm,0);
        temp-=dinic();
        if(temp>n) {
            ans=i-1;
            break;
        }
    }
    /*init();
    for(int i=1;i<=ans;i++){
        for(int j=1;j<i;j++){
            if(check(i+j)){
                add_edge(j,i+maxm,1);
                add_edge(maxm+i,j,0);
            }
        }
        add_edge(s,i,1);
        add_edge(i,s,0);
        add_edge(i+maxm,t,1);
        add_edge(t,i+maxm,0);
    }
    dinic();*/
    //这里可以不用重新建边跑网络流,只需要在输出路径时不输出多余的一个球即可
    printf("%d\n",ans);
    print(t);
    system("pause");
    return 0;
}

猜你喜欢

转载自blog.csdn.net/blue_kid/article/details/80750310