1. 程式人生 > >[bzoj2668] [洛谷P3159] [cqoi2012] 交換棋子

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

next names OS esc 分類 答案 Go pool esp

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;   
}

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