【网络流24题】魔术球问题

博客探讨了如何利用网络流算法解决Luogu 2765题目,即在满足相邻球编号和为平方数的条件下,确定最大的球编号x及放球方案。该问题被解释为最小路径覆盖的具体应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Luogu 2765

题意

n个柱子,上面可以放球。要求相邻两个球的编号和是一个平方数。现在要将编号为1~x的所有球放在这n个柱子上。问最大的x可以是多少并输出放球的方案。

题解

就是最小路径覆盖的具体应用,两个相加为平方数的小球之间连边即可。

代码

#include<bits/stdc++.h>
#define MAXN 3000+1109
#define MAXM 4000000+1109
#define INF 0x3fffffff

using namespace std;

int n,m;

int S,T;
int dis[MAXN],dl[MAXN];
int Head[MAXN],Next[MAXM],To[MAXM],FB[MAXM],Flow[MAXM],Cnt=0;//MAXM要考虑反边 
int cur[MAXN];//当前弧优化 

int b[MAXN];

void ADD(int x,int y,int z)
{
    Cnt++;Next[Cnt]=Head[x];Head[x]=Cnt;To[Cnt]=y;FB[Cnt]=Cnt+1,Flow[Cnt]=z;
    Cnt++;Next[Cnt]=Head[y];Head[y]=Cnt;To[Cnt]=x;FB[Cnt]=Cnt-1,Flow[Cnt]=0;
}
bool BFS(int Begin,int End)
{
    int t=0,w=1,x,X;
    memset(dis,0xff,sizeof(dis));
    dis[Begin]=0;dl[1]=Begin;
    do
    {
        x=dl[++t];
        for(int i=Head[x];i;i=Next[i])
        {
            X=To[i];
            if(Flow[i]<=0||dis[X]>=0) continue;
            dis[X]=dis[x]+1;dl[++w]=X;
        }
    }while(t<w);
    if(dis[End]>0) return true;
    else return false;
}
int Find(int x,int MFLOW,int y)
{
    if(x==y) return MFLOW;
    int X,h; 
    for(int i=cur[x];i;i=Next[i])
    {
        cur[x]=i;
        X=To[i];
        if(Flow[i]>0&&dis[x]+1==dis[X]&&(h=Find(X,min(MFLOW,Flow[i]),y)))
        {
            Flow[i]-=h;
            Flow[FB[i]]+=h;
            return h;
        }
    }
    return 0;
}
int Solve(int x,int y)
{
    int X,Ans=0;
    while(1)
    {
        if(!BFS(x,y)) break;
        for(int i=S;i<=T;i++)//初始化全体cur 
            cur[i]=Head[i];
        while((X=Find(x,0x7fffffff,y)))
            Ans+=X;
    }
    return Ans;
}

void print(int x,int y)
{
    b[x]=true;
    printf("%d ",x);
    for(int i=Head[x];i;i=Next[i])
        if(Flow[i]==0&&To[i]!=0)
            {print(To[i]-y,y);return ;}
}

void Build(int x)
{
    Cnt=0;
    S=0;T=2*x+1;
    Head[S]=Head[T]=0;
    for(int i=1;i<=x;i++)
    {
        Head[i]=Head[i+x]=0;
        ADD(S,i,1);ADD(i+x,T,1);
    }
    int X;
    for(int i=1;i<x;i++)
        for(int j=i+1;j<=x;j++)
        {
            X=sqrt(i+j);
            if(X*X!=i+j) continue;
            ADD(i,j+x,1);
        }
}

int main()
{
    scanf("%d",&n);
    int l=1,r=1600,mid;
    do
    {
        mid=(l+r)/2;
        Build(mid);
        if(mid-Solve(S,T)<=n) l=mid+1;
        else r=mid;
    }while(l!=r);
    l--; 
    Build(l);
    Solve(S,T);
    printf("%d\n",l);
    b[0]=true;
    for(int j=1;j<=l;j++)
        if(!b[j])
            print(j,l),printf("\n");
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值