• 【BZOJ2668】交换棋子(CQOI2012)-费用流+拆点


    测试地址:交换棋子
    做法:本题需要使用费用流+拆点。
    容易想到,把全部白色棋子都移动到目标位置的话,剩下的黑色棋子也自然都移动到目标位置了,因此我们只考虑白色棋子。又注意到一枚棋子的移动可以看成一份流量,这样问题就转化成了网络流问题,因此我们要考虑如何将题目中的限制条件表示出来。
    注意到,一枚棋子在移动的路径中,交换的次数是它经过的点数1,而对每个点的影响是,对于起点和终点使用了1次交换,对于路径中的其他点使用了2次交换。我们发现这个很难用传统的拆点办法来搞定,因为点作为起点,终点,起始点时的贡献是不一样的。我们进一步思考,发现这三种情况等价于:仅从该点出,仅进入该点,既进入该点又从该点出。因此我们把一个点拆成三个点,在它们之间顺次连边,如果从第一个点进入第二个点,表示路径进入了该点,如果从第二个点进入第三个点,表示路径从该点出,这样我们就能表现它们的贡献了。考虑三种情况:
    1.该点起始状态和终止状态相同,那么该点的第二个点的入度和出度应该相等,所以两条边的容量限制均为limit2
    2.该点上原来是白子,需要变成黑子,那么该点的第二个点的出度比入度大1,所以两条边的容量应该依次为limit2,limit+12
    3.该点上原来是黑子,需要变成白子,那么该点的第二个点的入度比出度大1,所以两条边的容量应该依次为limit+12,limit2
    这样我们就把每个点交换次数的限制表现了出来。那么我们再开一个源点和一个汇点,从源点向所有原来是白子的点的第二个点连容量为1的边,从所有要变成白子的点的第二个点向汇点连容量为1的边,再从每个点的第三个点向与它相邻的点的第一个点连容量为inf的边,这样这个网络的每一种流都对应着一种合法的方案了。
    那么怎么求最小的解呢?只需要把第三个点向第一个点连的边附上1的费用,做费用流即可。
    以下是本人代码:

    #include <bits/stdc++.h>
    using namespace std;
    const int inf=1000000000;
    int n,m,S,T,first[2010]={0},tot=1,sum1=0,sum2=0;
    int dis[2010],last[2010],laste[2010];
    char in[21][21],out[21][21],val[21][21];
    bool vis[2010]={0};
    struct edge
    {
        int v,next,f,c;
    }e[100010];
    queue <int> Q;
    
    int point(int x,int y,int type)
    {
        return (x-1)*m+y+type*n*m;
    }
    
    void insert(int a,int b,int f,int c)
    {
        e[++tot].v=b,e[tot].next=first[a],e[tot].f=f,e[tot].c=c,first[a]=tot;
        e[++tot].v=a,e[tot].next=first[b],e[tot].f=0,e[tot].c=-c,first[b]=tot;
    }
    
    void init()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
            scanf("%s",in[i]+1);
        for(int i=1;i<=n;i++)
            scanf("%s",out[i]+1);
        for(int i=1;i<=n;i++)
            scanf("%s",val[i]+1);
        S=3*n*m+1,T=3*n*m+2;
    
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
            {
                if (in[i][j]-'0') insert(S,point(i,j,1),1,0),sum1++;
                if (out[i][j]-'0') insert(point(i,j,1),T,1,0),sum2++;
    
                int limit=val[i][j]-'0';
                if (!(in[i][j]-'0')&&out[i][j]-'0') insert(point(i,j,0),point(i,j,1),(limit+1)/2,0);
                else insert(point(i,j,0),point(i,j,1),limit/2,0);
                if (in[i][j]-'0'&&!(out[i][j]-'0')) insert(point(i,j,1),point(i,j,2),(limit+1)/2,0);
                else insert(point(i,j,1),point(i,j,2),limit/2,0);
    
                for(int ki=-1;ki<=1;ki++)
                    for(int kj=-1;kj<=1;kj++)
                    {
                        if (ki==0&&kj==0) continue;
                        if (i+ki<1||i+ki>n||j+kj<1||j+kj>m) continue;
                        insert(point(i,j,2),point(i+ki,j+kj,0),inf,1);
                    }
            }
    }
    
    bool spfa()
    {
        for(int i=1;i<=T;i++)
            dis[i]=inf;
        dis[S]=0;
        Q.push(S);
        vis[S]=1;
        while(!Q.empty())
        {
            int v=Q.front();Q.pop();
            for(int i=first[v];i;i=e[i].next)
                if (e[i].f&&dis[v]+e[i].c<dis[e[i].v])
                {
                    dis[e[i].v]=dis[v]+e[i].c;
                    last[e[i].v]=v;
                    laste[e[i].v]=i;
                    if (!vis[e[i].v])
                    {
                        vis[e[i].v]=1;
                        Q.push(e[i].v);
                    }
                }
            vis[v]=0;
        }
        return dis[T]!=inf;
    }
    
    void mincost()
    {
        if (sum1!=sum2)
        {
            printf("-1");
            return;
        }
    
        int maxf=0,ans=0;
        while(spfa())
        {
            int x=T,minf=inf;
            while(x!=S)
            {
                minf=min(minf,e[laste[x]].f);
                x=last[x];
            }
            x=T;
            while(x!=S)
            {
                e[laste[x]].f-=minf;
                e[laste[x]^1].f+=minf;
                x=last[x];
            }
            maxf+=minf;
            ans+=minf*dis[T];
        }
    
        if (maxf<sum1) printf("-1");
        else printf("%d",ans);
    }
    
    int main()
    {
        init();
        mincost();
    
        return 0;
    }
  • 相关阅读:
    备忘录 | ‘神器’在手,新世界大门我有
    1001种玩法 | HotswapAgent:支持无限次重定义运行时类与资源
    面面观 | CentOS install etcd 测试
    控制台、终端、虚拟终端和伪终端
    KMP算法,BoyerMoore算法
    qemu kvm 虚拟化
    web端log4net输出错误日志到mysql
    JQuery调用WCF服务,部署在iis
    首篇
    微信支付接口
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793348.html
Copyright © 2020-2023  润新知