【網絡流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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章