NSOJ A fairy tale of the two(最小费用最大流、SPFA版本、ZKW版本)
n,m<=20,给两个n×m布尔矩阵,每次操作可将第一个矩阵的2个相邻元素互换。输出最少操作次数使得两个矩阵完全一样。
比赛的时候想过按照二分图完美匹配的类似做法构图,不过想到边太多以及卡各种题卡得没脾气,就先放放等赛后搞了。。。
看题解发现有更好的构图,所以就算当时搞了也是TLE。。。。
先对所有格子及其上下左右连条流量为inf,费用为1的边,表示使用这条边每次是花费1个操作。
然后将第一个矩阵为1的点连S,流量为1,费用0,表示这个1可以用于满足第二个矩阵的某一个需要
第二个矩阵为1的点连T,流量为1,费用0,表示满足了这个1的需求
答案就是满流的最小费用了。。
以前都是用SPFA版本的,第一次用ZKW版本。。。
参考:
ZKW: http://www.artofproblemsolving.com/blog/54262
DMute:http://blog.sina.com.cn/s/blog_76f6777d0101bbc8.html
这里算是把ZKW封装模板化,备用。。。
SPFA版本:1884ms
ZKW版本:276ms
ZKW使用的是选d[v]==d[u]+w(u,v)的思想
SPFA版本:
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #include <queue> using namespace std; #define ll long long #define maxn 444 #define maxe 55000 #define inf 0x3f3f3f3f struct Edge{ int u, v, nxt, cap, cost; }edge[maxe]; int head[maxn]; struct MinCostMaxFlow{ queue<int> que; int add; // edges number int vn; // total vertex number int cost[maxn], in[maxn], pre[maxn]; bool vis[maxn]; void init(int sz){ add = 0; vn = sz + 10; memset(head, -1, sizeof(head)); while (!que.empty()) que.pop(); } void insert(int u, int v, int w, int c){// u, v, capacity, cost edge[add].u = u; edge[add].v = v; edge[add].cap = w; edge[add].cost = c; edge[add].nxt = head[u]; head[u] = add++; edge[add].u = v; edge[add].v = u; edge[add].cap = 0; edge[add].cost = -c; edge[add].nxt = head[v]; head[v] = add++; } bool spfa(int s, int e){ memset(cost, 0x3f3f3f3f, sizeof(cost)); memset(in, 0, sizeof(in)); memset(vis, 0, sizeof(vis)); cost[s] = 0; pre[s] = -1; que.push(s); vis[s] = true; in[s]++; while (!que.empty()){ int u = que.front(); que.pop(); vis[u] = false; for (int i = head[u]; i != -1; i = edge[i].nxt){ int v = edge[i].v; if (edge[i].cap > 0 && cost[v] > cost[u] + edge[i].cost){ cost[v] = cost[u] + edge[i].cost; pre[v] = i; if (!vis[v]){ que.push(v); vis[v] = true; in[v]++; if (in[v] > vn) return false; } } } } return cost[e] < inf; } void mincostmaxflow(int s, int e, int &mincost, int &maxflow){ mincost = 0, maxflow = 0; while (spfa(s, e)){ int flow = inf; for (int i = pre[e]; i != -1; i = pre[edge[i].u]){ flow = min(flow, edge[i].cap); } maxflow += flow; for (int i = pre[e]; i != -1; i = pre[edge[i].u]){ edge[i].cap -= flow; edge[i ^ 1].cap += flow; } mincost += cost[e] * flow; } } }net; int a[22][22]; int b[22][22]; int dx[]={0,1,0,-1}; int dy[]={1,0,-1,0}; int main(){ int n,m; while(~scanf("%d%d",&n,&m)){ for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) scanf("%d",a[i]+j); for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) scanf("%d",b[i]+j); net.init(n*m+2); for(int i=1;i<=n;++i){ for(int j=1;j<=m;++j){ for(int k=0;k<4;++k){ int ii=i+dx[k], jj=j+dy[k]; int u=(i-1)*m+j, v=(ii-1)*m+jj; if(ii&&jj&&ii<=n&&jj<=m) net.insert(u,v,inf,1); } if(a[i][j]) net.insert(0,(i-1)*m+j,1,0); if(b[i][j]) net.insert((i-1)*m+j,n*m+1,1,0); } } int ans1=-1,ans2=-1; net.mincostmaxflow(0,n*m+1,ans1,ans2); printf("%d\n",ans1); } return 0; }
ZKW:
#include <cstdio> #include <cstring> #include <iostream> using namespace std; #define maxn 410 #define maxm 510000 #define inf 0x3f3f3f3f struct Edge{ int u,v,nxt,cap,cost; }edge[maxm]; int head[maxn]; struct ZKW_CostFlow{// 1-based int S,T; int add;// edges number bool vis[maxn]; int dis[maxn], slk[maxn]; int ans; void init(){ add=0; memset(head, -1, sizeof(head)); } void insert(int u, int v, int w, int c){// u, v, capacity, cost edge[add].u = u; edge[add].v = v; edge[add].cap = w; edge[add].cost = c; edge[add].nxt = head[u]; head[u] = add++; edge[add].u = v; edge[add].v = u; edge[add].cap = 0; edge[add].cost = -c; edge[add].nxt = head[v]; head[v] = add++; } int aug(int u,int f){ int left=f; if(u==T){ans+=f*dis[S];return f;} vis[u]=true; for(int i=head[u];i!=-1;i=edge[i].nxt){ int cap = edge[i].cap, v = edge[i].v; if(cap>0 && vis[v]==false){ int t = dis[v] + edge[i].cost - dis[u]; if(t==0){ int delt = aug(v, cap<left?cap : left); if(delt>0) edge[i].cap-=delt, edge[i^1].cap+=delt, left-=delt; if(left==0) return f; }else slk[v] = t<slk[v]?t:slk[v]; } } return f-left; } bool modlabel(int n){ int delt = inf; for(int i=1;i<=n;++i) if(!vis[i]) delt=min(delt,slk[i]), slk[i]=inf; if(delt==inf) return true; for(int i=1;i<=n;++i) if(vis[i]) dis[i]+=delt; return false; } void ZKW_Flow(int src,int des,int n,int& micost,int& maflow){ S=src, T=des; ans=micost=maflow=0; memset(dis,0,sizeof(dis)); memset(slk,0x3f,sizeof(slk)); do{ int tmp=0; do{ memset(vis,false,sizeof(vis)); maflow+=tmp; }while(tmp=aug(S,inf)); }while(!modlabel(n)); micost=ans; } }net;// 1-based int a[22][22]; int b[22][22]; int dx[]={0,1,0,-1}; int dy[]={1,0,-1,0}; int main(){ int n,m; while(~scanf("%d%d",&n,&m)){ for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) scanf("%d",a[i]+j); for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) scanf("%d",b[i]+j); net.init(); for(int i=1;i<=n;++i){ for(int j=1;j<=m;++j){ for(int k=0;k<4;++k){ int ii=i+dx[k], jj=j+dy[k]; int u=(i-1)*m+j, v=(ii-1)*m+jj; if(ii&&jj&&ii<=n&&jj<=m) net.insert(u,v,inf,1); } if(a[i][j]) net.insert(n*m+1,(i-1)*m+j,1,0); if(b[i][j]) net.insert((i-1)*m+j,n*m+2,1,0); } } int ans1=-1,ans2=-1; net.ZKW_Flow(n*m+1,n*m+2,n*m+2,ans1,ans2); printf("%d\n",ans1); } return 0; }