[bzoj2668] [洛谷P3159] [cqoi2012] 交换棋子

Description

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

Input

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

Output

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

Sample Input

3 3

110

000

001

000

110

100

222

222

222

Sample Output

4


想法

首先把题目说的不清楚的地方澄清一下:“第i行第j列的格子只能参与mi,j次交换”所说第i行第j列的棋子指的是每次交换后位于第i行第j列这个位置的棋子,可以是多个,而不是指最原始状态中第i行第j列那个特定的棋子。

很容易发现,我们可以只考虑白色棋子,只要它们都移动到目标状态,剩下的黑棋子也都到目标状态了。

不停地交换听起来好像比较棘手,但其实白棋子只有和身边的黑棋子交换位置才有用。

所以我们就是要给每个白棋子规划一条线路,让它们从原始位置变到目标位置。

很容易想到按原图建图,拆点~

但有个问题,若某个白格子经过某个格子,那么这个格子会被该白格子交换两次;而白格子原位置与目标位置只会被该白格子交换一次。

于是有一个更高级的拆点:一个点拆成三个!(id1,id2,id3)

id1到id2限制其他点与这个点交换的次数,id2到id3限制这个点与其他点交换的次数(即一个是进入的流量,一个是出去的流量)

建图,分类讨论。

  • 对于原状态和目标状态均为黑的格子。

    id1到id3连容量为cap/2,费用为1的边

  • 对于原状态为白,目标状态为黑的格子。

    S到id2连容量为1,费用为0的边

    id1到id2连容量为cap/2,费用为1的边

    id2到id3连容量为(cap+1)/2,费用为1的边

  • 对于原状态为黑,目标状态为白的格子。

    id2到T连容量为1,费用为0的边

    id1到id2连容量为(cap+1)/2,费用为1的边

    id2到id3连容量为cap/2,费用为1的边

  • 对于原状态和目标状态均为白的格子。

    S到id2连容量为1,费用为0的边

    id2到T连容量为1,费用为0的边

    id1到id2连容量为cap/2,费用为1的边

    id2到id3连容量为cap/2,费用为1的边

之后向八连通的格子连边。

注意是某格子的id1连向八连通格子的id3,然后八连通的id1连向这个格子的id3

。。。还是有点复杂的。

接下来就跑个最小费用最大流,看流量是否为总白格子数。

若不是,则输出-1,否则答案为 最小费用/2


代码

注意细节:

1.某些边的容量为(cap+1)/2,容易想错写成cap/2

2.数组要开够!!

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>

#define INF 1000000000

using namespace std;

const int N = 405;
const int M = 1205;

struct node{
    int v,f,c;
    node *next,*rev;
}pool[N*40],*h[M],*pree[M]; /**/
int cnt;
void addedge(int u,int v,int f,int c){
    node *p=&pool[++cnt],*q=&pool[++cnt];
    p->v=v;p->next=h[u];h[u]=p; p->f=f;p->c=c;p->rev=q;
    q->v=u;q->next=h[v];h[v]=q; q->f=0;q->c=-c;q->rev=p;
}

int S,T;
int d[M],vis[M],pre[M];
queue<int> que;
bool spfa(){
    int u,v;
    while(!que.empty()) que.pop();
    for(int i=S;i<=T;i++) d[i]=INF;
    d[S]=0; vis[S]=1; que.push(S);
    while(!que.empty()){
        u=que.front(); que.pop();
        vis[u]=0;
        for(node *p=h[u];p;p=p->next)
            if(p->f && d[v=p->v]>d[u]+p->c){
                d[v]=d[u]+p->c;
                pre[v]=u; pree[v]=p;
                if(!vis[v]){
                    vis[v]=1;
                    que.push(v);
                }
            }
    }
    return d[T]!=INF;
}
void MCMF(int &f,int &c){
    f=0; c=0;
    int u,w;
    while(spfa()){
        u=T; w=INF;
        while(u!=S){
            w=min(w,pree[u]->f);
            u=pre[u];
        }
        f+=w; c+=d[T]*w;
        u=T;
        while(u!=S){
            pree[u]->f-=w;
            pree[u]->rev->f+=w;
            u=pre[u];
        }
    }
}

int n,m;
char a[23][23],b[23][23],ch[23][23];
int dre[8][2]={-1,-1,-1,0,-1,1,0,-1,0,1,1,-1,1,0,1,1};

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=0;i<n;i++) scanf("%s",a[i]);
    for(int i=0;i<n;i++) scanf("%s",b[i]);
    for(int i=0;i<n;i++) scanf("%s",ch[i]);

    int w=n*m,id1,id2,id3,tot=0;
    S=0; T=w*3+1;
    for(int i=0;i<n;i++)
        for(int j=0;j<m;j++){
            id1=i*m+j+1; id2=id1+w; id3=id2+w;
            if(a[i][j]==‘0‘ && b[i][j]==‘0‘)
                addedge(id1,id3,(ch[i][j]-‘0‘)/2,2);
            else if(a[i][j]==‘1‘ && b[i][j]==‘0‘){
                tot++;
                addedge(S,id2,1,0);
                addedge(id1,id2,(ch[i][j]-‘0‘)/2,1);
                addedge(id2,id3,(ch[i][j]-‘0‘+1)/2,1); /**/
            }
            else if(a[i][j]==‘0‘ && b[i][j]==‘1‘){
                addedge(id2,T,1,0);
                addedge(id1,id2,(ch[i][j]-‘0‘+1)/2,1); /**/
                addedge(id2,id3,(ch[i][j]-‘0‘)/2,1);
            }
            else{
                tot++;
                addedge(S,id2,1,0); addedge(id2,T,1,0);
                addedge(id1,id2,(ch[i][j]-‘0‘)/2,1);
                addedge(id2,id3,(ch[i][j]-‘0‘)/2,1);
            }
            for(int k=0;k<8;k++){
                int x=i+dre[k][0],y=j+dre[k][1];
                if(x>=0 && x<n && y>=0 && y<m){
                    addedge(id3,x*m+y+1,INF,0);
                    addedge(x*m+y+1+2*w,id1,INF,0);
                }
            }
        }
    int f,c;
    MCMF(f,c);
    if(f<tot) printf("-1\n");
    else printf("%d",c/2);

    return 0;
}

原文地址:https://www.cnblogs.com/lindalee/p/8598122.html

时间: 2024-10-10 14:09:02

[bzoj2668] [洛谷P3159] [cqoi2012] 交换棋子的相关文章

洛谷P3159 [CQOI2012]交换棋子

巧妙的拆点方式,首先把1看成黑点,0看成空的,几次交换就可以看成一条路径 1)从容量上看,这条路径为1-2-2-2-2-2----2-1 2)从费用上看,这条路径每条边费用都是1 于是用一种巧妙的拆点方式,把一个点拆成三个,连两条边,成为一条链, 然后如果是黑点的话就由s向中间那个点连边,如果是路过的话就由一条链的尾部向另一条链的首部连边 这样就满足了上面的条件1)2) 容量的话如果是黑点出来就是(c+1)/2,进来是c/2,其他的以此类推 1 #include<cstdio> 2 #incl

p3159 [CQOI2012]交换棋子

传送门 分析 https://www.luogu.org/blog/dedicatus545/solution-p3159 代码 #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<algorithm> #include<cctype> #include<cmath> #include<cstdlib>

BZOJ2668: [cqoi2012]交换棋子

题解: 可以戳这里:http://www.cnblogs.com/zig-zag/archive/2013/04/21/3033485.html 其实自己yy一下就知道这样建图的正确性了. 感觉太神奇,居然还能拆成3个点 orzzzzzzzzzzzzzzzzzzzzzzzzz 跪跪跪跪跪跪跪跪 代码: 1 #include<cstdio> 2 3 #include<cstdlib> 4 5 #include<cmath> 6 7 #include<cstring&

[CQOI2012]交换棋子(最小费用最大流)

[CQOI2012]交换棋子(luogu) Description 题目描述 有一个n行m列的黑白棋盘,你每次可以交换两个相邻格子(相邻是指有公共边或公共顶点)中的棋子, 最终达到目标状态.要求第i行第j列的格子只能参与mi,j次交换. 输入格式 第一行包含两个整数n,m(1<=n, m<=20).以下n行为初始状态,每行为一个包含m个字符的01串, 其中0表示黑色棋子,1表示白色棋子.以下n行为目标状态,格式同初始状态. 以下n行每行为一个包含m个0~9数字的字符串,表示每个格子参与交换的次

BZOJ 2668 [cqoi2012]交换棋子 | 最小费用最大流

传送门 BZOJ 2668 题解 同时分别限制流入和流出次数,所以把一个点拆成三个:入点in(x).中间点mi(x).出点ou(x). 如果一个格子x在初始状态是黑点,则连(S, mi(x), 1, 0) 如果x在目标状态是黑点,则连(mi(x), T, 1, 0) 设x的交换次数限制是w 如果x在两种状态中颜色相同,则连(in(x), mi(x), w / 2, 0), (mi(x), ou(x), w / 2, 0) 如果x只在初始状态为黑色,则连(in(x), mi(x), w / 2,

[CQOI2012]交换棋子

---题面--- 题解: 其实还算是道好题 一开始很快想出了一个接近正解的建图方法,但其实是错误的,不过还是骗了70分_(:зゝ∠)_ 首先我们可以观察到棋子有限,但费用多种,其实也就相当于限制了流量,找最小费用 对于初始状态的每一个1,我们连s ---> x   flow = 1  cost = 0 对于目标状态的每一个1,我们连x ---> t  flow = 1 cost = 0 对于每一个方块,我们向周围八个格子连边 flow = inf , cost = 1(表示交换了一次) 然后就

【题解】CQOI2012交换棋子

感受到网络流的强大了--这道题目的关键在于: 前后颜色不变的,流入流出的次数相等:原本是黑色的最后变成了白色,流出比流入次数多1:原本是白色最后变成黑色,流入比流出次数多一.所以我们将每一点拆成3个点,分别代表流入点,原点与流出点.最开始为黑色的点与源点连流量为1,费用为0的边,最后为黑色的点与汇点连流量为1,费用为0的边. #include<bits/stdc++.h> using namespace std; #define maxn 300 #define maxm 8000 #defi

【BZOJ-2668】交换棋子 最小费用最大流

2668: [cqoi2012]交换棋子 Time Limit: 3 Sec  Memory Limit: 128 MBSubmit: 1055  Solved: 388[Submit][Status][Discuss] Description 有一个n行m列的黑白棋盘,你每次可以交换两个相邻格子(相邻是指有公共边或公共顶点)中的棋子,最终达到目标状态.要求第i行第j列的格子只能参与mi,j次交换. Input 第一行包含两个整数n,m(1<=n, m<=20).以下n行为初始状态,每行为一个

洛谷P1466 集合 Subset Sums

洛谷P1466 集合 Subset Sums这题可以看成是背包问题 用空间为 1--n 的物品恰好填充总空间一半的空间 有几种方案 01 背包问题 1.注意因为两个交换一下算同一种方案,所以最终 要 f [ v ] / 2 2.要开 long long 1 #include <cstdio> 2 #include <cstdlib> 3 #include <cmath> 4 #include <cstring> 5 #include <string&g