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

2668: [cqoi2012]交换棋子

Time Limit: 3 Sec  Memory Limit: 128 MB
Submit: 1055  Solved: 388
[Submit][Status][Discuss]

Description

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

Input

第一行包含两个整数nm(1<=nm<=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

HINT

Source

Solution

这是一道建图比较有趣的费用流.

想到用费用流比较容易,很显然,连法必然是S连向原图中的黑点,新图中的黑点连向T,问题在于中间的连边,如何利用容量限制两个点。

自己一开始立马想到拆点,但是是很naive的一拆二,限制容量,但这样并不可以。

实际上这个题需要一个点拆成3个点,我们把点$x$拆成$x_{1},x_{2},x_{3}$,并连出$x_{1}-->x_{2}-->x_{3}$

其中$x_{1}-->x_{2}$表示$x$这个点最多流入的流量,$x_{2}-->x_{3}$表示$x$这个点最多流出的流量。

对于一个点$x$,如果只是原图的黑点,那么限制$<x_{1},x_{2}>:cap=\frac{use[x]}{2};cost=0 ; <x_{2},x_{3}>:cap=\frac{use[x]+1}{2};cost=0$

并且连$S-->x_{2} : cap=1;cost=0$

对于一个点$x$,如果只是新图的黑点,那么限制$<x_{1},x_{2}>:cap=\frac{use[x]+1}{2};cost=0 ; <x_{2},x_{3}>:cap=\frac{use[x]}{2};cost=0$

并且连$x_{2}-->T : cap=1;cost=0$

如果一个点$x$,如果在原图和新图中都是白点或者都是黑点,那么我们限制$<x_{1},x_{2}>:cap=\frac{use[x]}{2};cost=0 ; <x_{2},x_{3}>:cap=\frac{use[x]}{2};cost=0$

对于图中的八连通格两两$x ; y$,连边$x_{3}-->y_{1} :cap=INF ; cost=1$流经此边,表示交换一次。

上述的限制方法,实际上是对网格的每个位置的流量变化进行讨论得到的,因为是一个最值情况,所以很多流入流出限制都无法完全流满。

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
#define MAXM 100010
#define MAXN 2000
int N,M,used[50][50];
char st[50][50],ed[50][50],us[50][50];
struct EdgeNode{int next,to,cap,cost;}edge[MAXM];
int cnt=1,head[MAXN];
inline void AddEdge(int u,int v,int w,int c) {cnt++; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].to=v; edge[cnt].cap=w; edge[cnt].cost=c;}
inline void InsertEdge(int u,int v,int w,int c) {AddEdge(u,v,w,c); AddEdge(v,u,0,-c);}
int dis[MAXN],S,T,Cost,Flow; 
bool mark[MAXN];
#define INF 0x7fffffff
inline bool SPFA()
{
    memset(mark,0,sizeof(mark));
    for (int i=S; i<=T; i++) dis[i]=INF;
    queue<int>q;
    q.push(S); dis[S]=0; mark[S]=1;
    while (!q.empty())
        {
            int now=q.front(); q.pop(); mark[now]=0;
            for (int i=head[now]; i; i=edge[i].next)
                if (edge[i].cap && dis[edge[i].to]>dis[now]+edge[i].cost)
                    {
                        dis[edge[i].to]=dis[now]+edge[i].cost;
                        if (!mark[edge[i].to]) q.push(edge[i].to),mark[edge[i].to]=1;
                    }
        }
    return dis[T]!=INF;
}
inline int DFS(int loc,int low)
{
    mark[loc]=1;
    if (loc==T) return low;
    int used=0,w;
    for (int i=head[loc]; i; i=edge[i].next)
        if (edge[i].cap && !mark[edge[i].to] && dis[edge[i].to]==dis[loc]+edge[i].cost)
            {
                w=DFS(edge[i].to,min(edge[i].cap,low-used));
                edge[i].cap-=w; edge[i^1].cap+=w; used+=w; Cost+=w*edge[i].cost;
                if (low==used) return used;
            }
    return used;
}
inline int zkw()
{
    int re=0;
    while (SPFA())
        {
            mark[T]=1;
            while (mark[T]) memset(mark,0,sizeof(mark)),re+=DFS(S,INF);
        }
    return re;
}
int id[50][50][4],ID,black,white;
int dx[10]={-1,-1,-1,0,0,1,1,1},dy[10]={-1,0,1,-1,1,-1,0,1};
inline bool check(int x,int y) {return x>=1 && x<=N && y>=1 && y<=M;}
void BuildGraph()
{
    for (int i=1; i<=N; i++)
        for (int j=1; j<=M; j++)
            for (int k=1; k<=3; k++)
                id[i][j][k]=++ID;
    S=0,T=ID+1;
    for (int i=1; i<=N; i++)
        for (int j=1; j<=M; j++)
            {
                if (st[i][j]=='0' && ed[i][j]=='1')
                    black++,
                    InsertEdge(S,id[i][j][2],1,0),
                    InsertEdge(id[i][j][1],id[i][j][2],used[i][j]/2,0),
                    InsertEdge(id[i][j][2],id[i][j][3],(used[i][j]+1)/2,0);
                if (st[i][j]=='1' && ed[i][j]=='0')
                    white++,
                    InsertEdge(id[i][j][2],T,1,0),
                    InsertEdge(id[i][j][1],id[i][j][2],(used[i][j]+1)/2,0),
                    InsertEdge(id[i][j][2],id[i][j][3],used[i][j]/2,0);
                if (st[i][j]==ed[i][j])
                    InsertEdge(id[i][j][1],id[i][j][2],used[i][j]/2,0),
                    InsertEdge(id[i][j][2],id[i][j][3],used[i][j]/2,0);
            }
    for (int i=1; i<=N; i++)
        for (int j=1; j<=M; j++)
            for (int x,y,k=0; k<8; k++)
                {
                    x=i+dx[k],y=j+dy[k];
                    if (check(x,y)) InsertEdge(id[i][j][3],id[x][y][1],INF,1);
                }
}
int main()
{
    scanf("%d%d",&N,&M);
    for (int i=1; i<=N; i++) scanf("%s",st[i]+1);
    for (int i=1; i<=N; i++) scanf("%s",ed[i]+1);
    for (int i=1; i<=N; i++) scanf("%s",us[i]+1);
    for (int i=1; i<=N; i++)
        for (int j=1; j<=M; j++) used[i][j]=us[i][j]-'0';
    BuildGraph();
    Flow=zkw();
    printf("%d\n",black==white? (Flow==black? Cost : -1) : -1);
    return 0;
} 

脑残RE了好几次...

posted @ 2016-10-14 20:19  DaD3zZ  阅读(525)  评论(0编辑  收藏  举报