[费用流]luogu P3159 交换棋子
https://www.luogu.org/problemnew/show/P3159
分析
这题更神仙
看到点有出入限制,我们考虑拆点
但既然从棋盘某点入,就要从棋盘某点出
所以一个点被拆成了三个,in,mid,out,其中mid连接源汇
然后从s到初始图的1点连条边,目标图的1点向t连条边,流量均为1
然后再给每个点向其八联通连边,费用为1,流量为1
这时候问题来了,我们发现一个点与源点相连时,它参与交换的次数为1,与汇点相连时同样,但是作为交换的中间点,它们参与交换的次数为2
那我们考虑给流量/2,但是这样问题又出现了,如果它与汇点相连,那么进入的次数要+1,同源点相连时类似
所以我们给流量除时,考虑给原流量加上1再除(当且仅当与汇点相连)
此题如此神仙,实是妙哉!
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <cstdio> #include <queue> #include <memory.h> using namespace std; const int N=21; const int Inf=2147483647; struct Pipe { int v,c,w,nx; }g[500*N*N]; int cnt=1,list[3*N*N]; int dis[3*N*N],f[3*N*N]; bool vis[3*N*N]; int n,m,s,t,ans,w,need; char a[N][N],b[N][N]; void Add(int u,int v,int c,int w) { g[++cnt]=(Pipe){v,c,w,list[u]};list[u]=cnt; g[++cnt]=(Pipe){u,0,-w,list[v]};list[v]=cnt; } bool SPFA() { queue<int> q; while (!q.empty()) q.pop(); memset(vis,0,sizeof vis);memset(dis,0x3f,sizeof dis); q.push(s);vis[s]=1;dis[s]=0; while (!q.empty()) { int u=q.front();q.pop(); for (int i=list[u];i;i=g[i].nx) if (g[i].c&&dis[g[i].v]>dis[u]+g[i].w) { dis[g[i].v]=dis[u]+g[i].w;f[g[i].v]=i; if (!vis[g[i].v]) q.push(g[i].v); vis[g[i].v]=1; } vis[u]=0; } return dis[t]!=0x3f3f3f3f; } void MCF() { int x=t,mf=Inf; while (f[x]) { mf=min(mf,g[f[x]].c); x=g[f[x]^1].v; } x=t;ans+=dis[t]; while (f[x]) { g[f[x]].c-=mf;g[f[x]^1].c+=mf; x=g[f[x]^1].v; } } void Dinic() { while (SPFA()) MCF(); } int main() { scanf("%d%d",&n,&m);s=0;t=3*n*m+1; for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) do { scanf("%c",&a[i][j]); } while (a[i][j]!='0'&&a[i][j]!='1'); for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) { do { scanf("%c",&b[i][j]); } while (b[i][j]!='0'&&b[i][j]!='1'); a[i][j]-=48;b[i][j]-=48; if (a[i][j]) Add(s,2*n*m+(i-1)*m+j,1,0),w++; if (b[i][j]) Add(2*n*m+(i-1)*m+j,t,1,0),need++; } if (w!=need) { printf("-1"); return 0; } need=0; for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) { char c; do { scanf("%c",&c); } while ('0'>c||c>'9'); c-=48; if (c) { Add((i-1)*m+j,2*n*m+(i-1)*m+j,(c+(b[i][j]>a[i][j]))/2,0); Add(2*n*m+(i-1)*m+j,n*m+(i-1)*m+j,(c+(a[i][j]>b[i][j]))/2,0); } } for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) { if (i>1) { Add(n*m+(i-2)*m+j,(i-1)*m+j,Inf,1); if (j>1) Add(n*m+(i-2)*m+j-1,(i-1)*m+j,Inf,1); if (j<m) Add(n*m+(i-2)*m+j+1,(i-1)*m+j,Inf,1); } if (i<n) { Add(n*m+i*m+j,(i-1)*m+j,Inf,1); if (j>1) Add(n*m+i*m+j-1,(i-1)*m+j,Inf,1); if (j<m) Add(n*m+i*m+j+1,(i-1)*m+j,Inf,1); } if (j>1) Add(n*m+(i-1)*m+j-1,(i-1)*m+j,Inf,1); if (j<m) Add(n*m+(i-1)*m+j+1,(i-1)*m+j,Inf,1); } Dinic(); for (int i=list[t];i;i=g[i].nx) if (!g[i].c) { printf("-1"); return 0; } printf("%d",ans); }
在日渐沉没的世界里,我发现了你。