博客园 首页 私信博主 显示目录 隐藏目录 管理 动画

洛谷 P2765 魔术球问题 解题报告

思路比较有趣,也比较新颖。

但想通了其实ok。


 IDEA: 

  1.  首先,很容易想到球数和柱子数正相关。因为加球一定不会使柱子数减少。
  2. 这样的话,我们从第一个球开始加入图中,每一个球拆成入点和出点,s->i*2=1,i*2+1->t=1 (用i*2和i*2+1表示的原因是:动态加点不知道总球数)
  3. 然后,每加一个新的球,我们试图让它和之前的球连边:
    1. 设这个球是now,设平方和为 i*i。则之前的球为i*i-now
    2. 显然0<i*i-now<now,即now<i*i<2*now。所以i从sqrt(now)+1开始枚举,(i*i-now)*2->now*2+1连边。
    3. 每次连边后跑dinic,如果有增流则说明不需要添加新柱子,能放在之前的球上。
    4. 如果没有则说明新的球没有地方放了,加一个柱子,当柱子数超过n的时候退出循环,柱子数--
    5. 每次加柱子时记录下这个柱子最下面放的球(也就是当前球)。
    6. 输出时,枚举每个柱子,从最下面的球开始递归找球输出。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <queue>
#define ll long long
#define space putchar(' ')
#define endl putchar('\n')
#define debug puts("------------------------")
#define F(i,x,n) for(int i=x;i<=n;++i)
#define F_(i,x,n) for(int i=x;i>=n;--i)
using namespace std;
inline void read(int &a) {a=0;int c=getchar(),b=1; while(c>'9'||c<'0') {if(c=='-')b=-1;c=getchar();} while(c>='0'&&c<='9') a=(a<<3)+(a<<1)+c-48,c=getchar();a*=b; }
inline int  Rem() {int a=0,c=getchar(),b=1; while(c>'9'||c<'0') {if(c=='-')b=-1;c=getchar();} while(c>='0'&&c<='9') a=(a<<3)+(a<<1)+c-48,c=getchar();return a*=b; }
inline void write(int x) {if(x>9)write(x/10);putchar('0'+x%10);}
inline void W(int x) {if(x<0){putchar('-'),x=-x;}write(x);}
/**/
const int N=4000,inf=0x3f3f3f3f;
int head[N],cnt=1,cur[N],n,s,t,dep[N],maxflow;
int num,rec[N],now;
bool vis[N];
char a,b;
struct edge
{
    int nxt,to,c;
}e[N*N];
/**/
namespace DINIC
{
    inline void add_one(int u,int v,int c)
    {
        e[++cnt]=(edge){head[u],v,c};
        head[u]=cnt;
    }
    inline void add(int u,int v,int c)
    {
        add_one(u,v,c);
        add_one(v,u,0);
    }
    inline int bfs()
    {
        memset(vis,0,sizeof(vis));
        memset(dep,0x3f,sizeof(dep));
        memcpy(cur,head,sizeof(head));
        queue<int>q;
        q.push(s);
        dep[s]=0,vis[s]=1;
        while(!q.empty())
        {
            int u=q.front();q.pop();vis[u]=0;
            for(int i=head[u];i!=-1;i=e[i].nxt)
            {
                int v=e[i].to;
                if(dep[v]>dep[u]+1&&e[i].c)
                {
                    dep[v]=dep[u]+1;
                    if(!vis[v])
                    {
                        vis[v]=1;
                        q.push(v);
                    }                    
                }
            }
        }
        return dep[t]!=inf;
    }
    int dfs(int u,int flow)
    {
        if(u==t)
        {
            vis[t]=1;
            maxflow+=flow;
            return flow;
        }
        int used=0,minflow=0;
        for(int i=cur[u];i!=-1;i=e[i].nxt)
        {
            int v=e[i].to;
            if(e[i].c&&dep[v]==dep[u]+1)
            {
                if((minflow=dfs(v,min(flow-used,e[i].c)))!=0)
                {
                    used+=minflow;
                    e[i].c-=minflow;
                    e[i^1].c+=minflow;
                    if(used==flow) break;
                }
            }
        }
        return used;
    }
    int Dinic()
    {
        maxflow=0;
        while(bfs())
        {
            vis[t]=1;
            while(vis[t])
            {
                vis[t]=0;
                dfs(s,inf);
            }
        }
        return maxflow;
    }
}using namespace DINIC;

void out(int u)
{
    for(int i=head[u];i;i=e[i].nxt)
    {
        int v=e[i].to;
        if(v!=s&&v!=t&&e[i].c==0)
        {
            cout<<(v-1)/2<<' ';
            out((v-1)/2*2);
        }
    }
}

int main()
{
    memset(head,-1,sizeof(head));
    read(n);
    s=0,t=1;
    while(num<=n)
    {
        ++now;
        add(s,now*2,1);
        add(now*2+1,t,1);
        for(int i=sqrt(now)+1;i*i<2*now;i++)
        {
            add((i*i-now)*2,now*2+1,1);
        }
        if(!Dinic()) rec[++num]=now;
    }
    --now;
    cout<<now<<'\n';
    for(int i=1;i<=n;i++)
    {
        cout<<rec[i]<<' ';out(rec[i]*2);endl;
    }
    return 0;
}
View Code

 

posted @ 2019-03-18 15:51  楚泫  阅读(203)  评论(0编辑  收藏  举报