网络流24题(四)

网络流24题(4)

四、魔术球问题

题目描述

假设有 \(n\) 根柱子,现要按下述规则在这 \(n\) 根柱子中依次放入编号为 \(1\)\(2\)\(3\),...的球“

  1. 每次只能在某根柱子的最上面放球。

  2. 同一根柱子中,任何 \(2\) 个相邻球的编号之和为完全平方数。

试设计一个算法,计算出在 \(n\) 根柱子上最多能放多少个球。例如,在 \(4\) 根柱子上最多可放 \(11\) 个球。

对于给定的 \(n\),计算在 \(n\) 根柱子上最多能放多少个球。

输入格式

只有一行一个整数 \(n\),代表柱子数。

输出格式

本题存在 Special Judge

请将 \(n\) 根柱子上最多能放的球数以及相应的放置方案输出。

输出的第一行是球数。

接下来的 \(n\) 行,每行若干个整数,代表一根柱子上的球的编号,数字间用单个空格隔开。

题解

模型:

如果有\(k\)个球,那么建图,转换为\(DAG\)最小覆盖。

建图与实现:

我们可以这么建一张图:
对每一对\((i,j)\),如果\(i+j\)是一个平方数,那么我们就让\(i\)\(j\)之间连一条有向边,方向为小的指向大的,不难发现这是一张\(DAG\)图。
手玩之后发现,对于\(n\)个柱子的球的个数\(k\)公式为\(k =floor(\frac{n^2}{2})-1\)
(关于这个结论可以看这个证明,并且在这里面有一种贪心写法)
于是参照之前的写法写即可。

我的网络流方案于网上的不太相同,我的写法大概快了十倍。
网上的写法大概是不断的增加球,并对其不断地求最小路径覆盖,对于每一个路径覆盖的值都找最多的球数。更详细的方法可以找其他的资料查看。

#include <bits/stdc++.h>
using namespace std;

#define ll long long
const ll N = 3500,M = 2e5+5,inf = 0x3f3f3f3f;
ll head[N],cnt = 1;
struct Edge{ll to,w,nxt;}edge[M];

void add(ll u,ll v,ll w){
    edge[++cnt].to = v;
    edge[cnt].w = w;
    edge[cnt].nxt = head[u];
    head[u] = cnt;
}
ll n,m,s,t,lv[N],cur[N];
bool bfs(){
    memset(lv, -1, sizeof(lv));
    lv[s] = 0;
    memcpy(cur, head, sizeof(head));
    queue<ll> q;q.push(s);
    while (!q.empty()){
        int p = q.front();q.pop();
        for (ll eg = head[p]; eg; eg = edge[eg].nxt){
            ll to = edge[eg].to, vol = edge[eg].w;
            if (vol > 0 && lv[to] == -1)lv[to] = lv[p] + 1, q.push(to);
        }
    }
    return lv[t] != -1;
}
ll dfs(ll p = s, ll flow = inf){
    if (p == t)return flow;
    int rmn = flow;
    for (ll &eg = cur[p]; eg; eg = edge[eg].nxt){
        if (!rmn)break;
        int to = edge[eg].to, vol = edge[eg].w;
        if (vol > 0 && lv[to] == lv[p] + 1){
            ll c = dfs(to, min(vol, rmn));
            rmn -= c;
            edge[eg].w -= c;
            edge[eg ^ 1].w += c;
        }
    }
    return flow - rmn;
}
ll dinic(){
    ll ans = 0;
    while(bfs()) ans += dfs();
    return ans;
}
ll f[N],vis[N];
void print(){
    memset(f,-1,sizeof f);
    for(ll eg = head[0];eg;eg = edge[eg].nxt){
        if(edge[eg].w == 0){
            ll v = edge[eg].to;
            for(ll j = head[v];j;j = edge[j].nxt){
                if(edge[j].w == 0){
                    f[v] = edge[j].to-n;
                    vis[edge[j].to-n] = true;
                }
            }
        }
    }

    for(ll i = 1;i <= n;i++){
        if(!vis[i]){
            ll x = i;
            cout<<x;
            while(f[x]!=-1){
                cout<<' '<<f[x];
                x = f[x];
            }
            cout<<endl;
        }
    }
}
bool check(ll x){
    ll y = sqrt(x);
    return x == y*y;
}
int main(){
    ios::sync_with_stdio(false);
    cin>>n;n++;
    ll k = floor(n*n/2)-1;
    n = k;
    cout<<n<<endl;
    for(ll i = 1;i <= n;i++) {
        add(0,i,1);add(i,0,0);
        add(i+n,2*n+1,1);add(2*n+1,i+n,0);
    }
    for(ll i = 1;i <= n;i++){
        for(ll j = i+1;j <= n;j++){
            if(check(i+j)){
                add(i,j+n,1);
                add(j+n,i,0);
            }
        }
    }
    s = 0,t = 2*n+1;
    ll ss = dinic();
    print();
    return 0;
}
posted @ 2021-09-10 17:01  Paranoid5  阅读(27)  评论(0编辑  收藏  举报