http://www.elijahqi.win/2018/02/12/bzoj2668/
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
看到题 觉得和tjoi2013循环格非常像 那么就费用流咯 直接上来拆两个点乱写 wa飞 只有40 然后开始了膜题解之路 从中午一直看到现在菜死 才明白大概是哪里出了问题 另外..注意下 这题是八连通..
那么题解写到:我其实应该是拆点变成三个点 然后怎么搞首先1->2为限制最多换入的流量 2->3为最多换出的流量假设这个点原来是黑 然后后来变成了白点 这个限制说明我应该是对换入有一个限制 对换出也有一个限制 如果该点一开始是黑的后来变白了 说明他需要有至少1的流量把他换出才可 那么最后验证答案是否是-1的时候只需要验证我是否将所有不在位置上的点全部换出了 对于换到正确位置的关键就在于我这个流量最后从我需要的点流出去了 所以当原图只是黑点的时候 1->2 use[i]>>1 2->3 use[i]+1>>1 反之只需要改一改这个我的+1的位置即可 使得我这个点可以有流量流出 如果这个点初始状态和最终的状态一直相同 那么 首先我不能有一个交换次数是奇数次 这样我这个位置的数就不是原来的了 即 交换奇数次一定无法换回原来的状态
然后所有八连通的点都从3->1 如果这时候流量为1 那么说明这个被交换了一次
#include
#include
#include
#include
#define N 1500
#define N1 22
#define inf 0x3f3f3f3f
using namespace std;
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}
while(ch<='9'&&ch>='0') x=x*10+ch-'0',ch=getchar();
return x*f;
}
int num=1,h[N],flag[N],f[N],id[N1][N1],id1[N1][N1],id2[N1][N1];
int T,pre[N],path[N],n,m,used[N1][N1],mp[N1][N1],mp1[N1][N1];
int dx[]={-1,-1,-1,0,0,1,1,1},dy[]={-1,0,1,-1,1,-1,0,1};
struct node{
int y,z,next,c;
}data[N<<3];
inline void insert1(int x,int y,int z,int c){
data[++num].y=y;data[num].z=z;data[num].next=h[x];h[x]=num;data[num].c=c;
data[++num].y=x;data[num].z=0;data[num].next=h[y];h[y]=num;data[num].c=-c;
}
inline bool spfa(){
memset(f,0x3f,sizeof(f));f[0]=0;memset(pre,-1,sizeof(pre));deque<int>q;
q.push_back(0);memset(flag,0,sizeof(flag));flag[0]=1;
while(!q.empty()){
int x=q.front();q.pop_front();flag[x]=0;
for (int i=h[x];i;i=data[i].next){
int y=data[i].y,c=data[i].c,z=data[i].z;
if (f[x]+cif (!flag[y]){
flag[y]=1;
if(!q.empty()&&f[y]else q.push_back(y);
}
}
}
}if (pre[T]==-1) return 0;else return 1;
}
char s[50];
int main(){
freopen("bzoj2668.in","r",stdin);
n=read();m=read();int tot=0;int cnt=0;
for (int i=1;i<=n;++i)
for (int j=1;j<=m;++j) id[i][j]=++tot,id1[i][j]=++tot,id2[i][j]=++tot;T=tot+1;
for (int i=1;i<=n;++i){
scanf("%s",s+1);
for (int j=1;j<=m;++j) {
mp[i][j]=s[j]-'0';//if (mp[i][j]) ++cnt;
for (int k=0;k<8;++k){
int x=i+dx[k],y=j+dy[k];
if (x<1||x>n||y<1||y>m) continue;
insert1(id2[i][j],id[x][y],inf,1);
}
}
}
for (int i=1;i<=n;++i){
scanf("%s",s+1);
for (int j=1;j<=m;++j) mp1[i][j]=s[j]-'0';
}
for (int i=1;i<=n;++i){
scanf("%s",s+1);
for (int j=1;j<=m;++j) used[i][j]=s[j]-'0';
}int cnt1=0;
for (int i=1;i<=n;++i){
for (int j=1;j<=m;++j){
if ((mp[i][j]&&mp1[i][j])||(!mp[i][j]&&!mp1[i][j]))
insert1(id[i][j],id1[i][j],used[i][j]>>1,0),
insert1(id1[i][j],id2[i][j],used[i][j]>>1,0);
if (!mp[i][j]&&mp1[i][j]){
++cnt;
insert1(0,id1[i][j],1,0);insert1(id[i][j],id1[i][j],used[i][j]>>1,0);
insert1(id1[i][j],id2[i][j],used[i][j]+1>>1,0);
}
if (mp[i][j]&&!mp1[i][j]){
++cnt1;
insert1(id1[i][j],T,1,0);insert1(id[i][j],id1[i][j],used[i][j]+1>>1,0);
insert1(id1[i][j],id2[i][j],used[i][j]>>1,0);
}
}
}
int ans=0,ans1=0;
while(spfa()){
int minn=inf,now=T;
while(now) minn=min(minn,data[path[now]].z),now=pre[now];ans+=minn;now=T;ans1+=f[T]*minn;
while(now) data[path[now]].z-=minn,data[path[now]^1].z+=minn,now=pre[now];
}
if (cnt1!=cnt) puts("-1");else
if (ans!=cnt) puts("-1");else printf("%d",ans1);
return 0;
}