题目:
题解:
能够看出来这道题目是网络流,但并不清楚这个图怎么建
这种有交换次数限制的要考虑分成两半:最多流入的数量,最多流出的数量 作为流量限制
我们对于每个节点拆成三个点:x1,x2,x3,一个点的流量限制为val[i][j]
整体的连接方向是x1->x2->x3
如果只有原图中这个点是黑点
<x1,x2>.cap=val[i][j]/2,cost=0;<x2,x3>.cap=(val[i][j]+1)/2,cost=0;
<
x
1
,
x
2
>
.
c
a
p
=
v
a
l
[
i
]
[
j
]
/
2
,
c
o
s
t
=
0
;
<
x
2
,
x
3
>
.
c
a
p
=
(
v
a
l
[
i
]
[
j
]
+
1
)
/
2
,
c
o
s
t
=
0
;
<script type="math/tex" id="MathJax-Element-3">< x1,x2 >.cap=val[i][j]/2,cost=0;< x2,x3 >.cap=(val[i][j]+1)/2,cost=0;</script>
然后 S−>x1,cap=1,cost=0 S − > x 1 , c a p = 1 , c o s t = 0
如果只有新图中这个点是黑点
<x1,x2>.cap=(val[i][j]+1)/2,cost=0;<x2,x3>.cap=val[i][j]/2,cost=0;
<
x
1
,
x
2
>
.
c
a
p
=
(
v
a
l
[
i
]
[
j
]
+
1
)
/
2
,
c
o
s
t
=
0
;
<
x
2
,
x
3
>
.
c
a
p
=
v
a
l
[
i
]
[
j
]
/
2
,
c
o
s
t
=
0
;
<script type="math/tex" id="MathJax-Element-5">< x1,x2 >.cap=(val[i][j]+1)/2,cost=0;< x2,x3 >.cap=val[i][j]/2,cost=0;</script>
然后 x2−>T,cap=1,cost=0 x 2 − > T , c a p = 1 , c o s t = 0
如果原图和新图这个点的颜色一样(都黑/白)
<x1,x2>.cap=val[i][j]/2,cost=0;<x2,x3>.cap=val[i][j]/2,cost=0;
<
x
1
,
x
2
>
.
c
a
p
=
v
a
l
[
i
]
[
j
]
/
2
,
c
o
s
t
=
0
;
<
x
2
,
x
3
>
.
c
a
p
=
v
a
l
[
i
]
[
j
]
/
2
,
c
o
s
t
=
0
;
<script type="math/tex" id="MathJax-Element-7">< x1,x2 >.cap=val[i][j]/2,cost=0;< x2,x3 >.cap=val[i][j]/2,cost=0;</script>
然后对于那些互相可以交换的点连接< x3,y1 >.cap=INF,cost=1,流过这条边表示交换一次。
然后我们愉快的跑最小费用最大流就好了,zkw优化一用就很顺手了。
代码:
#include <queue>
#include <cstdio>
#include <cstring>
#define INF 1e9
using namespace std;
const int N=2000;
const int cc[8][2]={{1,0},{1,1},{0,1},{-1,1},{-1,0},{-1,-1},{0,-1},{1,-1}};
int tot,point[N],dis[N],v[N*10],remind[N*10],c[N*10],nxt[N*10],yt[25][25],xt[25][25],val[25][25],ans;bool vis[N];
char st[25];
void addline(int x,int y,int z,int vv)
{
++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; remind[tot]=z; c[tot]=vv;
++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x; remind[tot]=0; c[tot]=-vv;
}
bool spfa(int s,int t)
{
queue<int>q;q.push(s);
memset(dis,0x7f,sizeof(dis));
memset(vis,0,sizeof(vis));
dis[s]=0;
while (!q.empty())
{
int now=q.front(); q.pop(); vis[now]=0;
for (int i=point[now];i!=-1;i=nxt[i])
if (dis[v[i]]>dis[now]+c[i] && remind[i])
{
dis[v[i]]=dis[now]+c[i];
if (!vis[v[i]]) vis[v[i]]=1,q.push(v[i]);
}
}
return dis[t]<INF;
}
int dfs(int now,int t,int limit)
{
vis[now]=1;
if (now==t || !limit) return limit;
int flow=0,f;
for (int i=point[now];i!=-1;i=nxt[i])
if (remind[i] && !vis[v[i]] && dis[v[i]]==dis[now]+c[i])
{
f=dfs(v[i],t,min(limit,remind[i]));
flow+=f;
limit-=f;
remind[i]-=f;
remind[i^1]+=f;
if (!limit) return flow;
}
return flow;
}
int zkw(int s,int t)
{
while (spfa(s,t))
{
memset(vis,0,sizeof(vis));
ans+=dis[t]*dfs(s,t,INF);
}
}
int main()
{
tot=-1;memset(point,-1,sizeof(point));
int n,m;scanf("%d%d",&n,&m);
int s=0,t=n*m*3+1;
for (int i=1;i<=n;i++)
{
scanf("%s",st+1);
for (int j=1;j<=m;j++) yt[i][j]=st[j]-'0';
}
for (int i=1;i<=n;i++)
{
scanf("%s",st+1);
for (int j=1;j<=m;j++) xt[i][j]=st[j]-'0';
}
for (int i=1;i<=n;i++)
{
scanf("%s",st+1);
for (int j=1;j<=m;j++) val[i][j]=st[j]-'0';
}int a=0,b=0;
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
{
int id=(i-1)*m+j;
if (yt[i][j]==xt[i][j]) addline(id,id+n*m,val[i][j]/2,0),addline(id+n*m,id+2*n*m,val[i][j]/2,0);
else if (xt[i][j]==1) a++,addline(id,id+n*m,(val[i][j]+1)/2,0),addline(id+n*m,id+2*n*m,val[i][j]/2,0),addline(id+n*m,t,1,0);
else if (yt[i][j]==1) b++,addline(id,id+n*m,val[i][j]/2,0),addline(id+n*m,id+2*n*m,(val[i][j]+1)/2,0),addline(s,id+n*m,1,0);
}
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
{
int id=(i-1)*m+j+2*n*m;
for (int k=0;k<8;k++)
{
int xx=i+cc[k][0],yy=j+cc[k][1],jd=(xx-1)*m+yy;
if (xx<=0 || yy<=0 || xx>n || yy>m) continue;
addline(id,jd,INF,1);
}
}
zkw(s,t);
if (a==b) printf("%d",ans);else printf("-1");
}