• P3159 [CQOI2012]交换棋子


    题目描述

    有一个n行m列的黑白棋盘,你每次可以交换两个相邻格子(相邻是指有公共边或公共顶点)中的棋子,最终达到目标状态。要求第i行第j列的格子只能参与mi,j次交换。

    输入格式

    第一行包含两个整数n,m(1<=n, m<=20)。以下n行为初始状态,每行为一个包含m个字符的01串,其中0表示黑色棋子,1表示白色棋子。以下n行为目标状态,格式同初始状态。以下n行每行为一个包含m个0~9数字的字符串,表示每个格子参与交换的次数上限。

    输出格式

    输出仅一行,为最小交换总次数。如果无解,输出-1。

    输入输出样例

    输入 #1
    3 3
    110
    000
    001
    000
    110
    100
    222
    222
    222
    输出 #1
    4

    思路

      对于棋盘的初始状态和最终状态可以看做二分图的两组点,而棋子的上下左右移动可以视作边的。交换黑白点可以视作讲黑子进行移动,每次费用为1,因此我们可以进行建图,但这题十分的特殊,因为它限制了每个格子的交换次数,并且用平常的拆点,我们无法表示它是否停留在改点,因此我们将1个点拆为3个点。分别为(left,now,right),每个点从left进入,停留在now上,从right出去。但对于两条边,它所限制的交换次数该如何划分又是一个问题。我们不妨把它视作黑点移动经过的次数。

      设limt表示限制数。

      对于初始和结果相同的点,边的为(l,now,limt/2,1),(now,r,limt/2,1)。

      对于初始为黑,结果为白的情况边为(l,now,limt/2,1),(now,r,(limt+1)/2,1)。

      对于初始为白,结果为黑的情况则与之相反。

    原理:如果开始为黑,结果为白,则黑点一定要移动出去,则若limt为奇数时,多余的1应该加入出边。若为偶数,则加1不会对其有影响。另一种情况与之相反。

    最后将源点和汇点连入图中,跑最大流最小费用即可。

    代码

    #include<bits/stdc++.h>
    #define N 10700
    #define M 107000
    #define inf 1<<29
    #define cnt (n*m*3)
    #define left(i,j) ((i-1)*m+j)
    #define now(i,j) (((i-1)*m+j)+n*m)
    #define right(i,j) (((i-1)*m+j)+(n*m<<1))
    using namespace std;
    struct node{
        int y,z,p,next;
    }e[M*2];
    int tot=1,head[N],maxflow=0,ans=0;
    int n,m,s,t;
    void add(int x,int y,int z,int p){
        e[++tot].y=y;e[tot].z=z;e[tot].p=p;e[tot].next=head[x];head[x]=tot;
        e[++tot].y=x;e[tot].z=0;e[tot].p=-p;e[tot].next=head[y];head[y]=tot;
    }
    int incf[N],v[N],pre[N],d[N];
    bool spfa(){
        queue<int> q;
        memset(d,0x3f,sizeof(d));// 0xcf
        memset(v,0,sizeof(v));
        q.push(s);d[s]=0;v[s]=1;
        incf[s]=inf;
        while(q.size()){
            int x=q.front();v[x]=0;q.pop();
            for(int i=head[x];i;i=e[i].next){
                int y=e[i].y,z=e[i].z;
                if(!z) continue;
                if(d[y]>d[x]+e[i].p){//d[y]<d[x]+e[i].p
                    d[y]=d[x]+e[i].p;
                    incf[y]=min(incf[x],z);
                    pre[y]=i;
                    if(!v[y]) v[y]=1,q.push(y);
                }
            }
        }
        if(d[t]==0x3f3f3f3f) return false;//0xcfcfcfcf
        return true;
    }
    void update(){
        int x=t;
        while(x!=s){
            int i=pre[x];
            e[i].z-=incf[t];
            e[i^1].z+=incf[t];
            x=e[i^1].y; 
        }
        maxflow+=incf[t];
        ans+=d[t]*incf[t];
    }
    int x1[N][N],x2[N][N],z[N][N];
    int dx[9]={0,-1,-1,-1,0,0,1,1,1};
    int dy[9]={0,-1,0,1,-1,1,-1,0,1};
    int main()
    {
        int t1=0,t2=0,ti,tj,k;char c[30];
        cin>>n>>m;s=0;t=cnt+1;
        for(int i=1;i<=n;i++){
            scanf("%s",c);
            for(int j=1;j<=m;j++){
                if(c[j-1]=='1'){
                    t1++;add(s,now(i,j),1,0);
                    x1[i][j]=1;
                }
            }
        }
        for(int i=1;i<=n;i++){
            scanf("%s",c);
            for(int j=1;j<=m;j++){
                if(c[j-1]=='1'){
                    t2++;add(now(i,j),t,1,0);
                    x2[i][j]=1;
                }
            }
        }
        if(t1!=t2){
            cout<<"-1"<<endl;return 0;
        }
        for(int i=1;i<=n;i++){
            scanf("%s",c);
            for(int j=1;j<=m;j++){
                t2=c[j-1]-'0';
                if(x1[i][j]==x2[i][j]) 
                    add(left(i,j),now(i,j),t2/2,0),add(now(i,j),right(i,j),t2/2,0);
                if(x1[i][j]&&!x2[i][j])
                    add(left(i,j),now(i,j),t2/2,0),add(now(i,j),right(i,j),(t2+1)/2,0);
                if(!x1[i][j]&&x2[i][j])
                    add(left(i,j),now(i,j),(t2+1)/2,0),add(now(i,j),right(i,j),t2/2,0);
            }
        }
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                for(k=1;k<=8;k++){
                    ti=i+dx[k];tj=j+dy[k];
                    if(ti<1||ti>n||tj<1||tj>m) continue;
                    add(right(i,j),left(ti,tj),inf,1);
                }
            }
        }
        
        while(spfa()) update();
        if(maxflow==t1) cout<<ans<<endl;
        else cout<<"-1"<<endl;
        return 0;
    }
  • 相关阅读:
    Oracle 查询语句截取字符串
    删除ORACLE数据库中重复的数据
    [易学C#]C#3.0语言新特性之匿名类型
    [讨论]程序之路在何方?
    [易学C#]C#3.0语言新特性之扩展方法
    [易学C#]C#3.0语言新特性之对象和集合初始化器
    用C#解决Oracle9i和Oracle10g字符集不兼容的问题
    一个C#操作Oracle的通用类
    [易学C#]C#3.0语言新特性之隐式类型
    重拾 DirectX 一:配置Visual Studio 2008+Microsoft DirectX SDK (June 2008) 开发环境
  • 原文地址:https://www.cnblogs.com/wangyiding2003/p/11569285.html
Copyright © 2020-2023  润新知