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

DMutehttp://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;
}
时间: 2024-10-11 20:58:01

NSOJ A fairy tale of the two(最小费用最大流、SPFA版本、ZKW版本)的相关文章

HDU1533 Going Home(最小费用最大流 spfa模版)

题意: 给你一个N行M列的矩阵,其中“.”代表空地,“H”代表房子,“m”代表人,其中有n个房子和n个人.现在要求每个人进入一间房子,且人走一步需要支付1美元. 求最小需要花费多少美元才能让所有人都进入到房子中(每个人只能进入一间房子,每个房子只能容纳一个人). 建图思路与安排工作那道题一样,设置一个超级源点(我习惯设置为0)和一个超级汇点,然后超级源点连接每个人(流量为1费用为0), 每个人连接每个房子(流量为1费用为人与房子的距离),然后每个房子连接超级汇点(流量为1费用为0). 跑一遍sp

【BZOJ3876】【Ahoi2014】支线剧情 有下界的最小费用最大流

#include <stdio.h> int main() { puts("转载请注明出处谢谢"); puts("http://blog.csdn.net/vmurder/article/details/43025375"); } [BZOJ2324]营救皮卡丘 这道题也是一道有下界的最小费用最大流. 我的题解地址:http://blog.csdn.net/vmurder/article/details/41378979 这道题其实就是模板题. 我的处理

POJ 3686.The Windy&#39;s 最小费用最大流

The Windy's Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 5477   Accepted: 2285 Description The Windy's is a world famous toy factory that owns M top-class workshop to make toys. This year the manager receives N orders for toys. The ma

P3381 【模板】最小费用最大流

P3381 [模板]最小费用最大流 题目描述 如题,给出一个网络图,以及其源点和汇点,每条边已知其最大流量和单位流量费用,求出其网络最大流和在最大流情况下的最小费用. 输入输出格式 输入格式: 第一行包含四个正整数N.M.S.T,分别表示点的个数.有向边的个数.源点序号.汇点序号. 接下来M行每行包含四个正整数ui.vi.wi.fi,表示第i条有向边从ui出发,到达vi,边权为wi(即该边最大流量为wi),单位流量的费用为fi. 输出格式: 一行,包含两个整数,依次为最大流量和在最大流量情况下的

C++之路进阶——最小费用最大流(支线剧情)

F.A.Qs Home Discuss ProblemSet Status Ranklist Contest ModifyUser  hyxzc Logout 捐赠本站 Notice:由于本OJ建立在Linux平台下,而许多题的数据在Windows下制作,请注意输入.输出语句及数据类型及范围,避免无谓的RE出现. 3876: [Ahoi2014]支线剧情 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 542  Solved: 332[Submit

hdu 4494 Teamwork 最小费用最大流

Teamwork Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=4494 Description Some locations in city A has been destroyed in the fierce battle. So the government decides to send some workers to repair these location

POJ - 2195 Going Home(最小费用最大流)

1.N*M的矩阵中,有k个人和k个房子,每个人分别进入一个房子中,求所有人移动的最小距离. 2.人看成源点,房子看成汇点,求最小费用最大流. 建图-- 人指向房子,容量为1,费用为人到房子的曼哈顿距离. 建立超级源点和超级汇点:超级源点指向人,容量为1,费用为0:超级汇点指向房子,容量为1,费用为0. 求超级源点到超级汇点的最小费用最大流即可. ps:容量为什么都设为1?---有待研究.. 3. 1.Bellman-Ford: #include<iostream> #include<st

hdu 1853 Cyclic Tour 最小费用最大流

题意:一个有向图,现在问将图中的每一个点都划分到一个环中的最少代价(边权和). 思路:拆点,建二分图,跑最小费用最大流即可.若最大流为n,则说明是最大匹配为n,所有点都参与,每个点的入度和出度又是1,所以就是环. /********************************************************* file name: hdu1853.cpp author : kereo create time: 2015年02月16日 星期一 17时38分51秒 *******

最小费用最大流

Farm Tour http://poj.org/problem?id=2135 建图再说吧 1 #include<cstdio> 2 #include<cstring> 3 #include<cstdlib> 4 #include<cmath> 5 #include<map> 6 #include<stack> 7 #include<queue> 8 #include<vector> 9 #include&l

POJ 2195 地图的最小费用最大流

思路:这题刚开始看就知道是最小费用最大流了,因为求出最优嘛,而且要m,H要一一对应,所以不是二分图匹配就是最小费用最大流. 不过,刚开始还在想每个m与H之间的最小花费如何求,难道要用dfs搜索吗?这样想之后看了下题目给的时间是1000ms,然后就把dfs搜索m与H之间的最短距离排除了.然后想了想,其实尼玛太简单了,因为题目说了只能垂直与竖直的走,所以最短距离不就是两个横坐标相减与两个纵坐标相减之和嘛! 然后每对m与H之间都连边,流量为1(因为每对匹配不能重复),费用为它们之间的距离即花费:然后建