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

来源:互联网 发布:tomcat9启动 源码解析 编辑:程序博客网 时间:2024/06/11 02:53

传送门

    魔术球
    题意:向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 0
原创粉丝点击