【luogu 2774】方格取数 (最小割)

题目链接

【题目大意】

有n*m的方格,在其中取任意个格子的数,保证最终结果最大,取得数字不能相邻

【题目思路】

如果不是知道是网络流的题,大概会试下爆搜,取之后周围的不可取之类的,但是显而易见会T

然后考虑怎么用网络流做,为什么能用网络流做

我自己对于网络流的理解,是解决选择之间的直接冲突,从而得到最优解的方法,而这个题每一个数字选和不选之间就会发生冲突

【建图】
如何实现选择

拆点,这里大多数程序都是将点根据(i+j)分成两类,我觉得是为了代码更简洁些,如果把所有点都拆成两个的话还是可行的

连边,将一类点和起点相连,一类点和终点相连,权值为对应点权值,将互相矛盾的点互相连接,权值为INF,

跑最大流/最小割即可,此处,对已有的图求割的结果,两边的点互相不连通,产生了使选择的点之间绝对不矛盾的最小权值

这里割掉的一定是和源汇点相邻的,而要断绝的目的就是令矛盾的点之间失去连接,所以最终最小割表示的就是不取的点的权值和

用总权值减去最小割即可

#include<cstdio>
#include<queue>
#include<cstring>
#define ll long long 
using namespace std;
const int MAXN = 5010;
const int MAXM = 200010;
const ll  INF = (1ll << 31) - 1;
ll sum = 0;
struct note
{
    int to;
    int nt;
    int rev;
    ll cal;
};
int nxt[4][2] = { {0,1},{1,0},{-1,0},{0,-1} };
struct edge
{
    note arr[MAXM];
    int siz;
    int maxn;
    int a[MAXN];
    int dp[MAXN];
    int  st[MAXN];
    int  dis[MAXN];
    int  cur[MAXN];
    int  depth[MAXN];
    int  top;
    int n, m, s, t;
    edge()
    {
        memset(st, -1, sizeof(st));
        memset(depth, -1, sizeof(depth));
        memset(dis, -1, sizeof(dis));
        top = 0;
    }
    void read()
    {
        scanf("%d%d", &n, &m);
        s = 0, t = siz = n * m + 1;
        for (int i = 1; i <= n; i++)
        {
            for (int j = 1; j <= m; j++)
            {
                int id; 
                ll x;
                scanf("%lld", &x);
                sum += x;
                id = (i - 1) * m + j;
                if ((i + j) & 1)
                    add( s,id, x);
                else
                    add(id, t, x);
            }
        }
        for (int i = 1; i <= n; i++)
        {
            for (int j = 1; j <= m; j++)
            {
                if ((i + j) & 1)
                {
                    for (int k = 0; k < 4; k++)
                    {
                        int nx = i + nxt[k][0], ny = j + nxt[k][1],id;
                        if (nx <= 0 || nx > n || ny <= 0 || ny > m) continue;
                        id = (i - 1) * m + j;
                        add(id, (nx - 1) * m + ny, INF);
                    }
                }

            }
        }
    }
    bool dep()
    {
        queue<int> q;
        q.push(s);
        memset(depth, -1, sizeof(depth));
        depth[s] = 0;
        while (!q.empty())
        {
            int v = q.front(); q.pop();
            for (int i = st[v]; i != -1; i = arr[i].nt)
            {
                int to = arr[i].to;
                if (!arr[i].cal)
                    continue;
                if (depth[to] != -1)
                    continue;
                depth[to] = depth[v] + 1;
                q.push(to);
            }
        }
        return (depth[t] != -1);

    }
    void add(int x, int y, ll z)
    {
        top++; arr[top] = { y,st[x],top + 1,z }; st[x] = top;
        top++; arr[top] = { x,st[y],top - 1,0 }; st[y] = top;
    }
    ll dfs(int now, ll val)
    {
        if (now == t || !val)
            return val;
        ll flow = 0;
        for (int& i = cur[now]; i != -1; i = arr[i].nt)
        {
            int to = arr[i].to;
            if (depth[to] != depth[now] + 1)
                continue;
            ll f = dfs(to, min(arr[i].cal, val));
            if (!f || !arr[i].cal)
                continue;
            flow += f;
            arr[i].cal -= f;
            arr[arr[i].rev].cal += f;
            val -= f;
            if (!val)
                return flow;
        }
        return flow;
    }
    ll dinic()
    {
        ll flow = 0;
        ll f;
        while (dep())
        {
            for (int i = 0; i <= siz; i++)
                cur[i] = st[i];
            while (f = dfs(s, INF))
                flow += f;
        }
        return flow;
    }
};
edge road, tmp;

void print(ll ans)
{
   printf("%lld", ans);
}
int main()
{
    road.read();
//    printf("%lld \n", sum);
    ll ans = sum - road.dinic();
    print(ans);
    return 0;
}
posted @ 2020-02-06 11:21  rentu  阅读(154)  评论(0编辑  收藏  举报