BZOJ2668: [cqoi2012]交换棋子(费用流)

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

解题思路:

这道题可以发现费用流肯定是可以解决的。
问题是怎么建图。
易知每步交换一定是一黑一白。
所以就可以考虑每个点前后的变化来得到白变黑次数的上限。
这个只与前后黑白状态有关。
若状态相同,则白变黑次数=黑变白次数。
不同则讨论即可。
一定是一个比另外一个大一。
这样就可以确定流入和流出上限。
现在考虑流量守恒,发现白黑相同时无流量变化,黑白不同时边权也守恒。
发现这样只需要新建两个新节点来达到建图的目的。
代码:
  1 #include<queue>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 typedef long long lnt;
  6 const int oo=0x3f3f3f3f;
  7 const int di[8]={0,0,-1,1,1,1,-1,-1};
  8 const int dj[8]={-1,1,0,0,-1,1,1,-1};
  9 struct pnt{
 10     int hd;
 11     int dis;
 12     int val;
 13     int pre;
 14     int lst;
 15     bool vis;
 16 }p[100000];
 17 struct ent{
 18     int twd;
 19     int lst;
 20     int vls;
 21     int dis;
 22 }e[1000000];
 23 int cnt;
 24 int n,m;
 25 int s,t;
 26 int maxflow;
 27 int no[101][101];
 28 int st[101][101];
 29 int ed[101][101];
 30 int vl[101][101];
 31 std::queue<int>Q;
 32 void ade_(int f,int t,int v,int d)
 33 {
 34     cnt++;
 35     e[cnt].twd=t;
 36     e[cnt].vls=v;
 37     e[cnt].dis=d;
 38     e[cnt].lst=p[f].hd;
 39     p[f].hd=cnt;
 40     return ;
 41 }
 42 void adde(int f,int t,int v,int d)
 43 {
 44     ade_(f,t,v,d);
 45     ade_(t,f,0,-d);
 46     return ;
 47 }
 48 bool Spfa(void)
 49 {
 50     while(!Q.empty())Q.pop();
 51     for(int i=1;i<=t;i++)
 52     {
 53         p[i].dis=p[i].val=oo;
 54         p[i].pre=-1;
 55         p[i].vis=false;
 56     }
 57     Q.push(s);
 58     p[s].vis=true;
 59     p[s].dis=0;
 60     while(!Q.empty())
 61     {
 62         int x=Q.front();
 63         Q.pop();
 64         p[x].vis=false;
 65         for(int i=p[x].hd;i;i=e[i].lst)
 66         {
 67             int to=e[i].twd;
 68             if(p[to].dis>p[x].dis+e[i].dis&&e[i].vls)
 69             {
 70                 p[to].dis=p[x].dis+e[i].dis;
 71                 p[to].pre=x;
 72                 p[to].lst=i;
 73                 p[to].val=std::min(p[x].val,e[i].vls);
 74                 if(p[to].vis)continue;
 75                 p[to].vis=true;
 76                 Q.push(to);
 77             }
 78         }
 79     }
 80     return p[t].pre!=-1;
 81 }
 82 int Ek(void)
 83 {
 84     int ans=0;
 85     while(Spfa())
 86     {
 87         maxflow+=p[t].val;
 88         ans+=p[t].dis*p[t].val;
 89         for(int i=t;i!=s;i=p[i].pre)
 90         {
 91             e[p[i].lst].vls-=p[t].val;
 92             e[((p[i].lst-1)^1)+1].vls+=p[t].val;
 93         }
 94     }
 95     return ans;
 96 }
 97 int main()
 98 {
 99     int a1(0),a2(0);
100     scanf("%d%d",&n,&m);
101     for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)no[i][j]=++cnt;
102     s=cnt*3+1,t=s+1;
103     cnt=0;
104     for(int i=1;i<=n;i++)
105         for(int j=1;j<=m;j++)
106             scanf("%1d",&st[i][j]),a1+=st[i][j];
107     for(int i=1;i<=n;i++)
108         for(int j=1;j<=m;j++)
109             scanf("%1d",&ed[i][j]),a2+=ed[i][j];
110     for(int i=1;i<=n;i++)
111         for(int j=1;j<=m;j++)
112             scanf("%1d",&vl[i][j]);
113     for(int i=1;i<=n;i++)
114     {
115         for(int j=1;j<=m;j++)
116         {
117             if(!st[i][j])adde(s,no[i][j],1,0);
118             if(!ed[i][j])adde(no[i][j],t,1,0);
119             int frm,twd;
120             if(st[i][j]==ed[i][j])frm=twd=vl[i][j]/2;
121             if(st[i][j]>ed[i][j])frm=(vl[i][j]+1)/2,twd=vl[i][j]/2;
122             if(st[i][j]<ed[i][j])twd=(vl[i][j]+1)/2,frm=vl[i][j]/2;
123             adde(no[i][j]+n*m,no[i][j],frm,0);
124             adde(no[i][j],no[i][j]+2*n*m,twd,0);
125             for(int d=0;d<8;d++)
126             {
127                 int ii=di[d]+i;
128                 int jj=dj[d]+j;
129                 if(!no[ii][jj])continue;
130                 adde(no[i][j]+2*n*m,no[ii][jj]+m*n,oo,1);
131                 
132             }
133         }
134     }
135     int ans=Ek();
136     if(m*n-maxflow!=a1||a1!=a2)ans=-1;
137     printf("%d\n",ans);
138     return 0;
139 }

 

posted @ 2019-02-26 22:36  Unstoppable728  阅读(184)  评论(0编辑  收藏  举报