【网络流24题】魔术球(最小路径覆盖+枚举)

传送门

    魔术球
    题意:向n根柱子里依次放置编号连续且递增的球。且同一根柱子里相邻两球编号和为完全平方数。求在这n根柱子里最多能放多少球。

I think

    模型转化:视n为路径覆盖数,枚举放入环中数的数量,转化为上一题的路径覆盖问题
    由于“依次”放球,所以构造出的一定是有向无环图。

Code

每次重新建边,不加优化的版本

#include<vector>
#include<cstdio>
#include<queue>
#include<cmath>
using namespace std;

const int sm = 4000;
const int sn = 127200;
const int C = 1600;
const int Inf = 0x3f3f3f3f;

int a,N,S=3201,T=3202;
int cnt,sum,ans,tot=1;
int to[sn],nxt[sn],hd[sm],w[sn];
int p[sm],lev[sm],cur[sm]; bool pf[3605];
vector<int>now[sm>>1],c[sm];

int Min(int x,int y) { return x<y?x:y; }
void Add(int u,int v) {
    to[++tot]=v,nxt[tot]=hd[u],hd[u]=tot,w[tot]=1;
    to[++tot]=u,nxt[tot]=hd[v],hd[v]=tot,w[tot]=0;
} 
void Init(int *g) {
    for(int i=1;i<=a;++i) g[i]=0;
    for(int i=C+1;i<=C+a;++i) g[i]=0;
    g[S]=g[T]=0;
}
bool Bfs() {
    queue<int>q;
    int t; q.push(S);
    Init(lev);lev[S]=1;
    while(!q.empty()) {
        t=q.front(),q.pop();
        for(int i=hd[t];i;i=nxt[i])
            if(!lev[to[i]]&&w[i]) {
                lev[to[i]]=lev[t]+1;
                if(to[i]==T) return 1;
                q.push(to[i]);
            }
    }
    return 0;
}
int Dfs(int x,int mx) {
    if(x==T||!mx) return mx;
    int f;
    for(int i=cur[x]?cur[x]:hd[x];i;i=nxt[i]) {
        cur[x]=i;
        if(lev[to[i]]==lev[x]+1&&w[i]) {
            if(f=Dfs(to[i],Min(mx,w[i])))
                return w[i]-=f,w[i^1]+=f,f;
        }
    }
    return 0;
}       
int Dinic() {
    int f,Flw=0;
    while(Bfs()) {
        Init(cur);
        while(f=Dfs(S,Inf))
            Flw+=f;
    }
    return Flw;
}
void Pre() {
    for(int i=hd[S];i;i=nxt[i]) 
        if(!w[i]) {
            for(int j=hd[to[i]];j;j=nxt[j])
                if(!w[j]&&to[j]!=S) {
                    p[to[j]]=to[i];
                    p[to[i]]=to[j];
                    break;
                }
        }
}
int main() {

    int u,v,ret=0;
    scanf("%d",&N);
    for(int i=1;i<=60;++i) pf[i*i]=1;

    while(1) {
        ++a;//枚举
        Init(hd);
        for(int i=1;i<a;++i)
            if(pf[i+a]) c[i].push_back(a);
        tot=1;
        for(int i=1;i<=a;++i) {//重新建边
            Add(S,i),Add(i+C,T);
            for(int j=0;j<c[i].size();++j)
                Add(i,c[i][j]+C);
        }
        ret=Dinic();
        if(a-ret==N) {//预备储存答案直到a-ret==N+1,此时取到最大答案
            Pre();
            ans=sum=0;
            for(int i=1;i<=a;++i)
                if(p[i]) {
                    now[++sum].clear();
                    now[sum].push_back(u=i);
                    do {
                        v=u,u=p[u]-C;
                        now[sum].push_back(u);
                        p[v]=0;
                    }while(p[u]);
                    ans+=now[sum].size();
                }
        }
        else 
        if(a-ret==N+1) break;
    }

    printf("%d\n",ans);
    for(int i=1;i<=sum;++i) {
        printf("%d",now[i][0]);
        for(int j=1;j<now[i].size();++j)
            printf(" %d",now[i][j]); 
        putchar(10);
    }
    return 0;
}

每次只在Dinic之后更新容量的优化版本,虽说从实际运行时间上来看优化不大,还是要mark一下这种将数组指定位复制的memcpy用法。

复制b[u..v]到a[x..y](其中y-x==v-u)
memcpy(a+x, b+u, sizeof(int)*(v-u+1));
#include<vector>
#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
using namespace std;

const int sm = 4000;
const int sn = 127200;
const int C = 1600;
const int Inf = 0x3f3f3f3f;

int a,N,S=3201,T=3202;
int cnt,sum,ans,tot=1;
int to[sn],nxt[sn],hd[sm],w[sn],_w[sn];
int p[sm],lev[sm],cur[sm]; bool pf[3605];
vector<int>now[sm>>1],c[sm];

int Min(int x,int y) { return x<y?x:y; }
void Add(int u,int v) {
    to[++tot]=v,nxt[tot]=hd[u],hd[u]=tot,_w[tot]=w[tot]=1;
    to[++tot]=u,nxt[tot]=hd[v],hd[v]=tot,_w[tot]=w[tot]=0;
} 
void Init(int *g) {
    for(int i=1;i<=a;++i) g[i]=0;
    for(int i=C+1;i<=C+a;++i) g[i]=0;
    g[S]=g[T]=0;
}

bool Bfs() {
    queue<int>q;
    int t; q.push(S);
    Init(lev);lev[S]=1;
    while(!q.empty()) {
        t=q.front(),q.pop();
        for(int i=hd[t];i;i=nxt[i])
            if(!lev[to[i]]&&w[i]) {
                lev[to[i]]=lev[t]+1;
                if(to[i]==T) return 1;
                q.push(to[i]);
            }
    }
    return 0;
}
int Dfs(int x,int mx) {
    if(x==T||!mx) return mx;
    int f;
    for(int i=cur[x]?cur[x]:hd[x];i;i=nxt[i]) {
        cur[x]=i;
        if(lev[to[i]]==lev[x]+1&&w[i]) {
            if(f=Dfs(to[i],Min(mx,w[i])))
                return w[i]-=f,w[i^1]+=f,f;
        }
    }
    return 0;
}       
int Dinic() {
    int f,Flw=0;
    while(Bfs()) {
        Init(cur);
        while(f=Dfs(S,Inf))
            Flw+=f;
    }
    return Flw;
}
void Pre() {
    for(int i=hd[S];i;i=nxt[i]) 
        if(!w[i]) {
            for(int j=hd[to[i]];j;j=nxt[j])
                if(!w[j]&&to[j]!=S) {
                    p[to[j]]=to[i];
                    p[to[i]]=to[j];
                    break;
                }
        }
}
int main() {
    int u,v,ret=0;
    scanf("%d",&N);
    for(int i=1;i<=60;++i) pf[i*i]=1;

    while(1) {
        ++a;
        memcpy(w+2,_w+2,sizeof(int)*(tot-1));
        for(int i=1;i<a;++i)
            if(pf[i+a]) Add(i,a+C);
        Add(S,a),Add(a+C,T);
        ret=Dinic();
        if(a-ret==N) {
            Pre();
            ans=sum=0;
            for(int i=1;i<=a;++i)
                if(p[i]) {
                    now[++sum].clear();
                    now[sum].push_back(u=i);
                    do {
                        v=u,u=p[u]-C;
                        now[sum].push_back(u);
                        p[v]=0;
                    }while(p[u]);
                    ans+=now[sum].size();
                }
        }
        else 
        if(a-ret==N+1) break;
    }

    printf("%d\n",ans);
    for(int i=1;i<=sum;++i) {
        printf("%d",now[i][0]);
        for(int j=1;j<now[i].size();++j)
            printf(" %d",now[i][j]); 
        putchar(10);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值