zoukankan      html  css  js  c++  java
  • BZOJ 3774 最优选择 (最小割)

    题面

    中文题面,不解释liao:传送门

    分析

    好难啊,好神啊,这题我理解了好久(可能还没有真正理解?)

    我们先假设所有点都满足条件得到了回报,回报之和为sumsum,那么最终的答案一定是sumsum减去一部分代价再减去一部分没有满足的点的回报.我们的任务就是求减去的一坨的最小值.所以我们将这个问题转化为求最小割的问题.

    现在看看怎么转化.首先因为是四联通,所以可以像国际象棋棋盘一样黑白点染色,看作二分图来连边.一个点如果要得到回报,要么是自己被控制,要么是自己不被控制且四周都被控制.那么分这两种情况拆点,拆成u0u_0u1u_1.

    假设uu是白点,vv是某个与uu相邻的黑点,括号内表示边权,连边的方式就是1.Su0(au)2.v0T(av)3.u0u1(bu)4.v1v0(bv)5.u1v0()6.u0v1()egin{aligned} &1.S o u_0 (a_u) \ &2.v_0 o T (a_v) \ &3.u_0 o u_1 (b_u) \ &4.v_1 o v_0 (b_v) \ &5.u1 o v0 (infty) \ &6.u0 o v1 (infty) \ end{aligned}
    画出来就是这个样子在这里插入图片描述
    因为是求最小割,那么最后必须割成 从STS o T没有增广路.下面看看这样建图有什么妙处

    • 因为中间两条红色的边权为infty,所以不可能被割掉.
    • 如果任意割掉两条灰色的边其一,那么这里就不会有增广路径,相当于控制了割掉的边对应的点并付出了相应代价.而此时如果没被割掉的点所相邻的四个点都已经割掉了灰色的边,那么这个点就被控制了,而且不用付出代价
    • 如果两个点都不被控制,那么这两个点一定都不能得到回报,也就是割掉两条蓝色的边的情况,相当于舍弃了这两个点的回报.
      如此一来我们巧妙地满足了题目的要求(反正我第一次做想不到).
      那么这样求一次最小割就能得到(要舍弃的回报 ++ 付出的代价) 的最小值,然后就求出了答案

    边一般我都开得很大…懒得算
    据说二分图的dinicdinic跑得很快.同机房高一清华一本爷(见友链)说O(nn)O(nsqrt n)(甩锅 )

    CODE

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    template<typename T>inline void read(T &num) {
        char ch; while((ch=getchar())<'0'||ch>'9');
        for(num=0;ch>='0'&&ch<='9';num=num*10+ch-'0',ch=getchar());
    }
    
    const int MAXN = 5010;
    const int MAXM = 100005;
    const int inf = 1e9;
    const int dx[] = { 0, 0, 1, -1 };
    const int dy[] = { 1, -1, 0, 0 };
    int n, m, S, T, a[55][55], b[55][55];
    int fir[MAXN], info[MAXN], cnt;
    struct edge { int to, nxt, c; }e[MAXM];
    inline void add(int u, int v, int cc) {
    	e[cnt] = (edge){ v, fir[u], cc }; fir[u] = cnt++;
    	e[cnt] = (edge){ u, fir[v], 0 }; fir[v] = cnt++;
    }
    int q[MAXN], vis[MAXN], cur, dis[MAXN];
    inline bool bfs() {
    	int head = 0, tail = 0;
    	vis[S] = ++cur; q[tail++] = S;
    	while(head < tail) {
    		int u = q[head++];
    		for(int i = fir[u]; ~i; i = e[i].nxt)
    			if(e[i].c && vis[e[i].to] != cur)
    				dis[e[i].to] = dis[u] + 1, vis[e[i].to] = cur, q[tail++] = e[i].to;
    	}
    	if(vis[T] == cur) memcpy(info, fir, sizeof fir);
    	return vis[T] == cur;
    }
    int dfs(int u, int Max) {
    	if(u == T || Max == 0) return Max;
    	int flow = 0, delta;
    	for(int &i = info[u]; ~i; i = e[i].nxt)
    		if(e[i].c && dis[e[i].to] == dis[u] + 1 && (delta=dfs(e[i].to, min(Max-flow, e[i].c)))) {
    			e[i].c -= delta, e[i^1].c += delta, flow += delta;
    			if(flow == Max) return flow;
    		}
    	return flow;
    }
    inline int dinic() {
    	int flow = 0, x;
    	while(bfs()) {
    		while((x=dfs(S, inf)))
    			flow += x;
    	}
    	return flow;
    }
    inline int enc(int i, int j, bool k) { return (i-1)*m + j + k*n*m; }
    inline void build() {
    	S = 2*n*m + 1, T = 2*n*m + 2;
    	for(int i = 1; i <= n; ++i)
    		for(int j = 1; j <= m; ++j) {
    			if((i+j) & 1) {
    				add(S, enc(i, j, 0), a[i][j]);
    				add(enc(i, j, 0), enc(i, j, 1), b[i][j]);
    			}
    			else {
    				add(enc(i, j, 0), T, a[i][j]);
    				add(enc(i, j, 1), enc(i, j, 0), b[i][j]);
    			}
    			for(int l = 0, x, y; l < 4; ++l)
    				if((x=i+dx[l]) >= 1 && x <= n && (y=j+dy[l]) >= 1 && y <= m)
    					if((i+j) & 1) add(enc(i, j, 0), enc(x, y, 1), inf), add(enc(i, j, 1), enc(x, y, 0), inf);
    		}
    }
    int main ()
    {
    	memset(fir, -1, sizeof fir);
        read(n), read(m);
    	for(int i = 1; i <= n; ++i)
    		for(int j = 1; j <= m; ++j)
    			read(a[i][j]);
    	int sum = 0;
    	for(int i = 1; i <= n; ++i)
    		for(int j = 1; j <= m; ++j)
    			read(b[i][j]), sum += b[i][j];
    	build();
    	printf("%d
    ", sum-dinic());
    }
    
  • 相关阅读:
    React生命周期
    React第三次入门
    滴滴新锐面经
    前端优化
    Z-index
    maven建ssh项目的pom文件
    拦截器与过滤器的区别
    Jquery的ajax获取action中的返回值
    清空数据库所有表的数据
    orcal操作锦集
  • 原文地址:https://www.cnblogs.com/Orz-IE/p/12039420.html
Copyright © 2011-2022 走看看