P2765 魔术球问题[二分图匹配]

魔术球问题

中文题干
这道题有点东西。。。

题目意思就不说了;
脑洞:首先要有一个判断完全平方数的函数,这个怎么判断也不说了。
这是网络流24题中的题,是要我们用网络流的知识来写吧。但是想用匈牙利来试试。。
这题有点动态的匈牙利的味道,就是这个二分图是在不断的变大的,直到他的最大独立点到达一个题目要求的值时,就停下来。
之前一直不知道怎么让他变大,一直觉得是不是加一次就要重新跑一遍算法。。
但后来怕是看到了大佬们的博客,有了点想法:
就是我们完全可以,将所有的数与他小的并且和是完全平方数的数相连一条边。
而每次加一个点的时候,我们就可以将这个点加进去,然后连边,最后只用对这个点进行匈牙利算法就可以了。。

然后想想,我们跑这个算法的时候,不就是一个点一个点的跑的吗。。。唉 太菜了。
至于如何输出序列,找我们的 l l l数组啊,里面早就给我们连好链了,等着我们输出呢。。。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>

#define len 20005
using namespace std;

struct edge{
    int u, v, nxt;
};

edge e[len];
int vis[len], l[len], vs[len];//vis数组和l数组都是匹配时候用的,vs数组在输出时候用
int head[len], hcnt;

void Init (){
    //memset(vis, 0, sizeof (vis));
    memset(l, 0, sizeof (l));
    memset(head, -1, sizeof (head));
    hcnt = 0;
}

void adde(int u, int v){
    e[hcnt].u = u; e[hcnt].v = v;
    e[hcnt].nxt = head[u]; head[u] = hcnt ++;
}

bool Find(int x){
    //cout << "!!!" << x << endl;
    for (int i=head[x]; ~i; i=e[i].nxt){
        int v = e[i].v;
        if (!vis[v]){
            vis[v] ++;
            if (!l[v] || Find(l[v])){
                l[v] = x; return true;
            }
        }
    }
    return false;
}

bool check(int x, int y){
    int tmp = sqrt(x + y);
    if (tmp * tmp == (x + y))
        return true;
    else return false;
}

int main(){
    int n;scanf("%d", &n);
    Init();
    int ans = 0, m = 0;
    do {
        m++;
        for (int i=m-1; i>=1; i--) if (check(i, m)) adde(m, i);
        
        memset(vis, 0, sizeof (vis));
        if (Find(m)) ans ++;//把新的点加进去
    }while(m - ans <= n);
    //以下是输出
    printf ("%d\n", m-1);
    for (int i=1; i<=n; i++){
        int ans = 1;
        while (vs[ans]) ans ++;
        while (l[ans]) {
            printf ("%d ", ans);
            vs[ans] ++;
            ans = l[ans];
        }
        printf ("%d \n", ans);
        ans = 1;
    }
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值