[网络流24题]魔术球问题

Description

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

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

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

求在$n$根柱子上最多能放多少个球。

Input

第$1$行有$1$个正整数$n$,表示柱子数。

Output

第一行是球数。

接下来的$n$行,每行是一根柱子上的球的编号。

Sample Input

4

Sample Output

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

HINT

$n\;\leq\;100$

Solution

枚举答案$ans$,在图中建立节点$1,2,...,ans$。如果对于$i,j,\;i<j$且$i+j$为一个完全平方数,建一条有向边$(i,j)$。该图是有向无环图,求最小路径覆盖。寻找最大的$ans$使得最小路径覆盖数$=n$。(枚举$ans$即可)

#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<cstdio>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define N 5005
#define M 200001
using namespace std;
struct graph{
    int nxt,to,f;
}e[M];
int a[N<<1],g[N<<1],dep[N<<1],l,m,s,t,fl,ans,cnt=1;
bool v[N];
queue<int> q; 
inline bool sq(int x){
    int k=sqrt(x);
    return k*k==x;
}
inline void addedge(int x,int y,int f){
    e[++cnt].nxt=g[x];g[x]=cnt;e[cnt].to=y;e[cnt].f=f;
}
inline void adde(int x,int y,int f){
    addedge(x,y,f);addedge(y,x,0);
}
inline bool bfs(int u){
    memset(dep,0,sizeof(dep));
    q.push(u);dep[u]=1;
    while(!q.empty()){
        u=q.front();q.pop();
        for(int i=g[u];i;i=e[i].nxt)
            if(e[i].f>0&&!dep[e[i].to]){
                q.push(e[i].to);
                dep[e[i].to]=dep[u]+1;
            }
    }
    return dep[t];
}
inline int dfs(int u,int f){
    int ret=0;
    if(u==t) return f;
    for(int i=g[u],d;i&&f;i=e[i].nxt)
        if(e[i].f>0&&dep[e[i].to]>dep[u]){
            d=dfs(e[i].to,min(e[i].f,f));
            e[i].f-=d;e[i^1].f+=d;f-=d;ret+=d;
        }
    return ret;
}
inline void dinic(){
    while(true){
        if(!bfs(s)) return;
        fl+=dfs(s,N);
    }
}
inline bool chk(int x){
    dinic();
    return x-fl<=m;
}
inline void find(int u){
    a[++l]=u;
    for(int i=g[u];i;i=e[i].nxt)
        if(e[i].to!=s&&!v[e[i].to-N]&&!e[i].f){
            v[e[i].to-N]=true;find(e[i].to-N);
        }
}
inline void Aireen(){
    scanf("%d",&m);ans=m;
    s=N-1;t=(N<<1)-1;
    for(int j=1;j<=ans;++j){
        adde(s,j,1);adde(j+N,t,1); 
        for(int i=1;i<j;++i)
            if(sq(i+j)) adde(i,j+N,1);
    }
    do{
        ++ans;
        adde(s,ans,1);adde(ans+N,t,1); 
        for(int i=1;i<ans;++i)
            if(sq(i+ans)) adde(i,ans+N,1);
    }while(chk(ans));
    printf("%d\n",ans-1);
    v[ans]=0;
    for(int i=1;i<ans;++i)
        if(!v[i]){
            l=0;v[i]=true;find(i);
            for(int j=1;j<=l;++j)
                printf("%d ",a[j]);
            printf("\n");
        }
}
int main(){
    freopen("ball.in","r",stdin);
    freopen("ball.out","w",stdout);
    Aireen();
    fclose(stdin);
    fclose(stdout);
    return 0;
}
posted @ 2017-01-01 11:06  Aireen_Ye  阅读(156)  评论(0编辑  收藏  举报
底部 顶部 留言板 归档 标签
Der Erfolg kommt nicht zu dir, du musst auf den Erfolg zugehen.