洛谷(魔术球问题)

链接:https://www.luogu.org/problemnew/show/P2765
思路:利用网络流可以在残留网络上跑的性质,我们考虑边跑网络流边加点,当当前点数-二分图的最大匹配数(将一个点拆为两个分别放在x和y上进行二分匹配,详见最小路径覆盖问题)>n时表示最小路径覆盖的数目超过了n,这时候停止达到球的最大值,最后出来查看一下所有的正向边flow不为0的,将每个点的下一个点记录出来,最后输出路径即可。
代码:

#include<bits/stdc++.h>
using namespace std;

int n,m;
const int maxn = 5010;
const int INF = 1e9;
int vis[maxn];
int nexto[maxn];

struct edge{
    int from,to,cap,flow;
};

struct Dinic{
    int n,m,s,t;
    vector<edge> edges;
    vector<int> G[maxn];
    bool vis[maxn];
    int d[maxn];
    int cur[maxn];

    void init(int n){
        this->n = n;
        edges.clear();
        for(int i=0;i<=n;i++)G[i].clear();
    }

    void addedge(int from,int to,int cap){
        edges.push_back(edge{from,to,cap,0});
        edges.push_back(edge{to,from,0,0});
        m = edges.size();
        G[from].push_back(m-2);
        G[to].push_back(m-1);
    }

    bool bfs(){
        memset(vis,0,sizeof(vis));
        queue<int> q;
        q.push(s);
        d[s] = 0;
        vis[s] = 1;
        while(!q.empty()){
            int x = q.front();
            q.pop();
            for(int i=0;i<G[x].size();i++){
                edge &e = edges[G[x][i]];
                if(!vis[e.to]&&e.cap>e.flow){
                    vis[e.to] = 1;
                    d[e.to] = d[x] + 1;
                    q.push(e.to);
                }
            }
        }
        return vis[t];
    }
    int dfs(int x,int a){
        if(x==t||a==0)return a;
        int flow = 0,f;
        for(int &i = cur[x];i<G[x].size();i++){
            edge &e = edges[G[x][i]];
            if(d[x] + 1 == d[e.to]&&(f=dfs(e.to,min(a,e.cap-e.flow)))>0){
                e.flow+=f;
                edges[G[x][i]^1].flow -=f;
                flow+=f;
                a-=f;
                if(a==0)break;
            }
        }
        return flow;
    }

    int maxflow(int s,int t){
        this->s = s;
        this->t = t;
        int flow = 0;
        while(bfs()){
            memset(cur,0,sizeof(cur));
            flow+=dfs(s,INF);
        }
        return flow;
    }
}solver;


set<int> all;

int main(){
    scanf("%d",&n);
    for(int i=0;i<=100;i++)all.insert(i*i);
    solver.init(5000);
    int now = 0;
    int res = 0;
    while(++now){
        solver.addedge(0,now,1);
        solver.addedge(now+2000,5000,1);//2000作为x和y的分界线
        for(int i=1;i<now;i++){
            if(all.count(i+now))
                solver.addedge(i,now+2000,1);
        }
        res+=solver.maxflow(0,5000);
        //printf("%d\n",res);
        if(now-res>n)break;
    }
    printf("%d\n",now-1);
      for(int i=0;i<solver.edges.size();i+=2){
        edge e = solver.edges[i];
        if(e.flow)nexto[e.from] = e.to>2000?e.to-2000:e.to;
    }
    for(int i=1;i<=now-1;i++){
        int u = i;
        if(!vis[u]){
            printf("%d",u);
            vis[u] = 1;
            u = nexto[u];
            while(u){
                vis[u] = 1;
                printf(" %d",u);
                u = nexto[u];
            }
            printf("\n");
        }
    }
    return 0;
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 点完名,被安排登录数据,一天都在寝室记数据,头都是昏的,实在累得不行时,才会看下书,缓解下心情,下午一个同事过生...
    舟舟style阅读 317评论 0 1
  • 1 我有个小学同学叫小夏,我俩二十多年没见面了。 去年年初有人招呼着要办同学会,所以顺势建了一个小学微信群。 平时...
    常馨月阅读 499评论 1 4
  • 善良,要有个度, 因为不是所有人都能明白你的感受。 善良久了, 你身边的人就会觉得, 你所做的一切都是你应该的。 ...
    大美汝城阅读 525评论 0 0