网络流24题——6.魔术球问题

题目链接

https://www.luogu.org/problemnew/solution/P2765

魔术球问题

这道题有很多奇奇怪怪的做法,比如打表,贪心,这些方法本文不进行讲解,本文讲解的是用最大流(二分图匹配)来解决。
讲讲博主的思考历程,如果把这个图建出来,编号小的向编号大的连边,条件是他们的和为平方数,那么得到一个DAG,每一根柱子相当于是一条图上的路径,每条路径之间不能有交集但是又要包含所有点,于是想到了最小路径覆盖,然后最小路径覆盖=顶点数-二分图最大匹配数,把模型简化之后,就是每次新加进来一个点,把他向所有比他小且与他的和为平方数的点连边,称这些点为关键点,如果一个关键点没有匹配,说明他位于一根柱子的顶端,那么我们把该关键点与新加进来这个点匹配就行了,如果一个关键点没匹配,那么他肯定是不位于顶端了。
最后输出方案的时候从前往后一直找匹配就行了。
博主的语文水平有限,如果看不懂的话可以进行代码理解。

#include<cstdio>
#include<algorithm>
#include<cctype>
#include<cstring>
#include<iostream>
#include<cmath>
#define LL long long
#define INF (2139062143)
#define N (100001)
using namespace std;
int n,now,k,ans;
int com[N],a[N],nxt[N],head[N];
bool vis[N];
template <typename T> void read(T&t) {
    t=0;
    bool fl=true;
    char p=getchar();
    while (!isdigit(p)) {
        if (p=='-') fl=false;
        p=getchar();
    }
    do {
        (t*=10)+=p-48;p=getchar();
    }while (isdigit(p));
    if (!fl) t=-t;
}
inline void add(int x,int y){
    a[++k]=y,nxt[k]=head[x],head[x]=k;
}
bool DFS(int x){
    for (int p=head[x];p;p=nxt[p]){
        if (!vis[a[p]]){
            vis[a[p]]=1;
            if (!com[a[p]]||DFS(com[a[p]])){
                com[a[p]]=x;
                return 1;
            }
        }   
    }
    return 0;
}
int main(){
    read(n);
    while (1){
        now++;
        for (int j=1;j<now;j++){
            int kk=sqrt(now+j);
            if (kk*kk==now+j){
                add(now,j);
            }
        }
        memset(vis,0,sizeof(vis));
        ans=ans+1-DFS(now);
        if (ans>n) break;
    }
    now--;
    printf("%d\n",now);
    memset(vis,0,sizeof(vis));
    for (int i=1;i<=now;i++){
        if (!vis[i]){
            vis[i]=1;
            int her=i;
            printf("%d ",her);
            while (com[her]){
                her=com[her];
                printf("%d ",her);
                vis[her]=1;
            }
            puts("");
        }
    } 
    return 0;
}

这里求二分图最大匹配用的是匈牙利算法,就是代码中的DFS.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值