bzoj 5403 Marshland

$n \leq 50$

 

sol:

放一个在 $x$ 处拐弯的 $L$ 形石头相当于在水平和垂直方向上各选一个与 $x$ 相邻的点,全局不能重复选

最小化危险度,相当于满足这些限制的情况下石头盖住的点危险度越大越好,而石头有各种各样的限制,考虑费用流

这是一个“只能增广 m 次的最大费用可行流”问题,我们增广到 m 次或者找出来的最长路为负即可

为了满足“不重复选”,可以拆点,每个入点向出点连流量 1,费用为危险度的边

因为每个危险点左右和上下各能选一个,相当于“每头牛都要分到一瓶可乐和一份午饭”(忘了这道题题号了...),可以把每个不危险的点分成“可乐”和“午饭”两类

因为选的两个不危险的点行数奇偶性不同,所以可以考虑按行数奇偶把非危险点分成两类

$S \space \rightarrow \space 每个行数为奇数的非危险点 \space \rightarrow \space 相邻的入点$

$相邻的出点 \space \rightarrow \space 每个行数为偶数的非危险点 \space \rightarrow \space T$

以上两类边流量 1,费用 0

然后愉快的流

#include<bits/stdc++.h>
#define LL long long
using namespace std;
inline int read()
{
    int x = 0,f = 1;char ch = getchar();
    for(;!isdigit(ch);ch = getchar())if(ch == '-') f = -f;
    for(;isdigit(ch);ch = getchar())x = 10 * x + ch - '0';
    return x * f;
}
const int maxn = 100010,maxm = 300010,oo = 2147483647;
struct ZKW
{
    int head[maxn], nx[maxn], inq[maxn], vis[maxn], dis[maxn];
    int n, m, s, t, ans, cost;
    queue<int> q;
    struct Edge
    {
        int from, to, caps, cost;
        Edge(){}
        Edge(int _1, int _2, int _3, int _4) : from(_1), to(_2), caps(_3), cost(_4){}
    }es[maxm];
    ZKW(){memset(head, -1, sizeof(head));}
    void setn(int _){n = _;}
    void AddEdge(int u, int v, int w, int c)
    {
        es[m] = Edge(u, v, w, c); nx[m] = head[u]; head[u] = m++;
        es[m] = Edge(v, u, 0, -c); nx[m] = head[v]; head[v] = m++;
    }
    bool BFS()
    {
        for(int i = 0;i <= n;i++)dis[i] = -oo;
        dis[t] = 0;inq[t] = 1;q.push(t);
        while(!q.empty())
        {
            int now = q.front();q.pop();
            for(int i = head[now]; i != -1; i = nx[i])
            {
                Edge& e = es[i^1];
                if(e.caps && dis[e.from] < dis[now] + e.cost)
                {
                    dis[e.from] = dis[now] + e.cost;
                    if(!inq[e.from])
                    {
                        inq[e.from] = 1;
                        q.push(e.from);
                    }
                }
            }
            inq[now] = 0;
        }
        if(dis[s] > 0){cost = dis[s];return 1;}
        return 0;
    }
    int DFS(int u, int a)
    {
        if(u == t || !a)return ans += cost * a, a;
        if(vis[u])return 0; vis[u] = 1;
        int flow = 0, f;
        for(int i = head[u]; i != -1; i = nx[i])
        {
            Edge& e = es[i];
            if(dis[e.to] == dis[u] - e.cost && (f = DFS(e.to, min(e.caps, a))))
            {
                e.caps -= f; es[i^1].caps += f;
                a -= f; flow += f;
                if(!a)return flow;
            }
        }
        return flow;
    }
    int MaxCostFlow(int _s, int _t, int tms)
    {
        s = _s, t = _t;
        int flow = 0, f;
        for(int i = 1; i <= tms; i++) if(BFS()) {memset(vis, 0, sizeof(vis)); flow += DFS(s, oo);}
        return flow;
    }
} sol;
int n, m, k, s, t, ans;
int mat[160][60],mem[160][160][5],dfn,b[maxn];
inline int pos(int x, int y, int type)
{
    return n * n * (type - 1) + (x - 1) * n + y;
}
int main()
{
    n = read(), m = read(), k = read();
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n; j++) mat[i][j] = read();
    s = 0, t = n * n * 2 + 1;
    sol.setn(t + 1);
    for(int i = 1; i <= k; i++)
    {
        int x = read(), y = read();
        b[pos(x, y, 1)] = 1;
    }
     
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n; j++)
        {
            ans += mat[i][j];
            if(b[pos(i, j, 1)])continue;
            if((i + j) & 1)
                sol.AddEdge(pos(i, j, 1), pos(i, j, 2), 1, mat[i][j]);
            else
            {
                if(i & 1)
                {
                    sol.AddEdge(s, pos(i, j, 1), 1, 0);
                    if(i > 1) sol.AddEdge(pos(i, j, 1), pos(i - 1, j, 1), 1, 0);
                    if(j > 1) sol.AddEdge(pos(i, j, 1), pos(i, j - 1, 1), 1, 0);
                    if(i < n) sol.AddEdge(pos(i, j, 1), pos(i + 1, j, 1), 1, 0);
                    if(j < n) sol.AddEdge(pos(i, j, 1), pos(i, j + 1, 1), 1, 0);
                }
                else
                {
                    sol.AddEdge(pos(i, j, 1), t, 1, 0);
                    if(i > 1) sol.AddEdge(pos(i - 1, j, 2),pos(i, j, 1), 1, 0);
                    if(j > 1) sol.AddEdge(pos(i, j - 1, 2),pos(i, j, 1), 1, 0);
                    if(i < n) sol.AddEdge(pos(i + 1, j, 2), pos(i, j, 1), 1, 0);
                    if(j < n) sol.AddEdge(pos(i, j + 1, 2), pos(i, j, 1), 1, 0);
                }
            }
        }
     
    sol.MaxCostFlow(s, t, m);
    cout << ans - sol.ans << endl;  
}
View Code

不知道为什么 在限制增广次数的时候 ZKW 的多路增广是错的

posted @ 2019-01-16 15:42  探险家Mr.H  阅读(241)  评论(0编辑  收藏  举报