[bzoj2668]交换棋子
基本思路是,要让所有黑点都相对应(所以首先判断黑点的个数)。
如果没有交换限制,可以按以下方法建图:源点向所有初始黑点连(1,0)的边,最终黑点向汇点连(1,0)的边,相邻的两点连边(inf,1),最小费用最大流即可。
考虑限制,我们把交换分为两种:(将1)交换进来/交换出去,因此需要两条边(三个点)来限制次数
将一个点拆成三个点,记作i1,i2和i3,设交换次数为s,分类讨论:1.如果初始和结束颜色相同,说明交换了偶数次(因为不会交换相邻两个颜色相同的点),i1连向i2(s/2,0)的边,i2连向i3(s/2,0)的边;2.如果初始是黑色,结束是白色,i1连向i2(s/2,0)的边,i2连向i3((s+1)/2,0)的边;3.如果初始是白色,结束是黑色,i1连向i2((s+1)/2,0)的边,i2连向i3(s/2,0)的边。最后还要将相邻的3向1连一条边,源点和汇点与2相连。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 2005 4 #define id (n*m+i*m+j+1) 5 #define oo 0x3f3f3f3f 6 struct ji{ 7 int nex,to,len,cost; 8 }edge[N*30]; 9 queue<int>q; 10 int E,n,m,s1,s2,t,head[N],v[25][25],d[N],vis[N],from[N]; 11 char s[N]; 12 void add(int x,int y,int z,int w){ 13 edge[E].nex=head[x]; 14 edge[E].to=y; 15 edge[E].len=z; 16 edge[E].cost=w; 17 head[x]=E++; 18 if (E&1)add(y,x,0,-w); 19 } 20 bool spfa(){ 21 memset(d,0x3f,sizeof(d)); 22 memset(vis,0,sizeof(vis)); 23 q.push(0); 24 d[0]=0; 25 while (!q.empty()){ 26 int k=q.front(); 27 q.pop(); 28 vis[k]=0; 29 for(int i=head[k];i!=-1;i=edge[i].nex){ 30 int v=edge[i].to; 31 if ((edge[i].len)&&(d[v]>d[k]+edge[i].cost)){ 32 d[v]=d[k]+edge[i].cost; 33 from[v]=i; 34 if (!vis[v]){ 35 vis[v]=1; 36 q.push(v); 37 } 38 } 39 } 40 } 41 return d[t]<0x3f3f3f3f; 42 } 43 int dinic(){ 44 int len,ans1=0,ans2=0; 45 while (spfa()){ 46 len=oo; 47 for(int i=t;i;i=edge[from[i]^1].to)len=min(len,edge[from[i]].len); 48 ans1+=len; 49 ans2+=len*d[t]; 50 for(int i=t;i;i=edge[from[i]^1].to){ 51 edge[from[i]].len-=len; 52 edge[from[i]^1].len+=len; 53 } 54 } 55 if (ans1<s1)ans2=-1; 56 return ans2; 57 } 58 int main(){ 59 scanf("%d%d",&n,&m); 60 memset(head,-1,sizeof(head)); 61 t=3*n*m+1; 62 for(int i=0;i<n;i++){ 63 scanf("%s",s); 64 for(int j=0;j<m;j++) 65 if (s[j]=='1'){ 66 s1++; 67 add(0,id,1,0); 68 v[i][j]++; 69 } 70 } 71 for(int i=0;i<n;i++){ 72 scanf("%s",s); 73 for(int j=0;j<m;j++) 74 if (s[j]=='1'){ 75 s2++; 76 add(id,t,1,0); 77 v[i][j]+=2; 78 } 79 } 80 for(int i=0;i<n;i++){ 81 scanf("%s",s); 82 for(int j=0;j<m;j++){ 83 add(id-n*m,id,(s[j]-'0'+(v[i][j]==2))/2,0); 84 add(id,id+n*m,(s[j]-'0'+(v[i][j]==1))/2,0); 85 for(int x=-1;x<2;x++) 86 for(int y=-1;y<2;y++) 87 if (((x)||(y))&&(i+x>-1)&&(i+x<n)&&(j+y>-1)&&(j+y<m))add(id+n*m,id+x*m+y-n*m,oo,1); 88 } 89 } 90 if (s1!=s2)printf("-1"); 91 else printf("%d",dinic()); 92 }