题解:
可以戳这里:http://www.cnblogs.com/zig-zag/archive/2013/04/21/3033485.html
其实自己yy一下就知道这样建图的正确性了。
感觉太神奇,居然还能拆成3个点
orzzzzzzzzzzzzzzzzzzzzzzzzz
跪跪跪跪跪跪跪跪
代码:
#include<cstdio> #include<cstdlib> #include<cmath> #include<cstring> #include<algorithm> #include<iostream> #include<vector> #include<map> #include<set> #include<queue> #include<string> #define inf 1000000000 #define maxn 200000+5 #define maxm 200000+5 #define eps 1e-10 #define ll long long #define pa pair<int,int> #define for0(i,n) for(int i=0;i<=(n);i++) #define for1(i,n) for(int i=1;i<=(n);i++) #define for2(i,x,y) for(int i=(x);i<=(y);i++) #define for3(i,x,y) for(int i=(x);i>=(y);i--) #define for4(i,x) for(int i=head[x],y;i;i=e[i].next) #define mod 1000000007 using namespace std; inline int read() { int x=,f=;char ch=getchar(); while(ch<''||ch>''){if(ch=='-')f=-;ch=getchar();} while(ch>=''&&ch<=''){x=*x+ch-'';ch=getchar();} return x*f; }
int n,m,k,mincost,tot,s,t,head[maxn],d[maxn],from[*maxm];
bool v[maxn];
queue<int>q;
int a[][][],num[][][],cnt[];
struct edge{int from,go,next,v,c;}e[*maxm];
void add(int x,int y,int v,int c)
{
e[++tot]=(edge){x,y,head[x],v,c};head[x]=tot;
e[++tot]=(edge){y,x,head[y],,-c};head[y]=tot;
}
bool spfa()
{
for (int i=s;i<=t;i++){v[i]=;d[i]=inf;}
q.push(s);d[s]=;v[s]=;
while(!q.empty())
{
int x=q.front();q.pop();v[x]=;
for (int i=head[x],y;i;i=e[i].next)
if(e[i].v&&d[x]+e[i].c<d[y=e[i].go])
{
d[y]=d[x]+e[i].c;from[y]=i;
if(!v[y]){v[y]=;q.push(y);}
}
}
return d[t]!=inf;
}
void mcf()
{
mincost=;
while(spfa())
{
int tmp=inf;
for(int i=from[t];i;i=from[e[i].from]) tmp=min(tmp,e[i].v);
mincost+=d[t]*tmp;
for(int i=from[t];i;i=from[e[i].from]){e[i].v-=tmp;e[i^].v+=tmp;}
}
}
const int dx[]={,,,-,,,-,-};
const int dy[]={,-,,,-,,,-}; int main() {
n=read();m=read();s=;t=*n*m+;
for0(k,)for1(i,n)for1(j,m)
{
char ch=getchar();
while(ch<''||ch>'')ch=getchar();
a[i][j][k]=ch-'';
//cout<<i<<' '<<j<<' '<<k<<' '<<a[i][j][k]<<endl;
num[i][j][k]=++tot;
cnt[k]+=a[i][j][k];
}
//cout<<tot<<' '<<s<<' '<<t<<endl;
tot=;
for1(i,n)for1(j,m)
{
if(a[i][j][]&&a[i][j][])a[i][j][]=a[i][j][]=;
if(a[i][j][])
{
add(num[i][j][],num[i][j][],a[i][j][]/,);
add(num[i][j][],num[i][j][],(a[i][j][]+)/,);
add(s,num[i][j][],,);
}
else if(a[i][j][])
{
add(num[i][j][],num[i][j][],(a[i][j][]+)/,);
add(num[i][j][],num[i][j][],a[i][j][]/,);
add(num[i][j][],t,,);
}
else
{
add(num[i][j][],num[i][j][],a[i][j][]/,);
add(num[i][j][],num[i][j][],a[i][j][]/,);
}
for0(k,)
{
int x=i+dx[k],y=j+dy[k];
if(x<||x>n||y<||y>m)continue;
add(num[i][j][],num[x][y][],inf,);
}
}
if(cnt[]!=cnt[]){printf("-1\n");return ;}
mcf();
printf("%d\n",mincost); return ; }
2668: [cqoi2012]交换棋子
Time Limit: 3 Sec Memory Limit: 128 MB
Submit: 673 Solved: 235
[Submit][Status]
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
110
000
001
000
110
100
222
222
222
Sample Output
4