费用流,LeetCode 2850. 将石头分散到网格图的最少移动次数

一、题目

1、题目描述

给你一个大小为 3 * 3 ,下标从 0 开始的二维整数矩阵 grid ,分别表示每一个格子里石头的数目。网格图中总共恰好有 9 个石头,一个格子里可能会有 多个 石头。

每一次操作中,你可以将一个石头从它当前所在格子移动到一个至少有一条公共边的相邻格子。

请你返回每个格子恰好有一个石头的 最少移动次数 。

2、接口描述

cpp
 ​
class Solution {
public:
    int minimumMoves(vector<vector<int>>& grid) {
        
    }
};

3、原题链接

2850. 将石头分散到网格图的最少移动次数


二、解题报告

1、思路分析

网络流建图就完了,考虑怎么建

首先虚拟源汇点s、t

对于石头数目c大于1的格子,从源点向其连容量为 c - 1, 费用为0的边

从该格子向所有石头数目为0的格子连容量为1,费用为曼哈顿距离的边

对于所有权值为0的格子,向汇点连容量为1,费用为0的边

跑最小费用最大流就行了

2、复杂度

时间复杂度: EK算法理论时间复杂度上限为O(NM^2)空间复杂度:O(N + M)

3、代码详解

cpp
 
constexpr int N = 15, inf = 1e9;
struct edge {
    int v, c, w, nxt;
} adj[N * 12];

int s, t;
int head[N], idx;
int q[N], dst[N], incf[N], pre[N];
bool vis[N];

void init() {
    memset(head, -1, sizeof head);
    idx = 0;
    s = 0, t = 10;
}

void addedge(int u, int v, int c, int w) {
    adj[idx] = { v, c, w, head[u] }, head[u] = idx ++;
}

void add(int u, int v, int c, int w) {
    addedge(u, v, c, w), addedge(v, u, 0, -w);
}

bool spfa() {
    memset(incf, 0, sizeof incf);
    memset(dst, 0x3f, sizeof dst);
    int f = 0, b = 0;
    dst[q[b ++] = s] = 0, vis[s] = true, incf[s] = inf;
    while (b - f) {
        int u = q[f ++];
        f %= N;
        vis[u] = false;
        for (int i = head[u]; ~i; i = adj[i].nxt) {
            int v = adj[i].v;
            if (adj[i].c && dst[v] > dst[u] + adj[i].w) {
                dst[v] = dst[u] + adj[i].w;
                incf[v] = std::min(incf[u], adj[i].c);
                pre[v] = i;
                if (vis[v]) continue;
                vis[q[b ++] = v] = true;
                b %= N;
            }
        }
    }
    return incf[t];
}

void update(int& f, int& c) {
    for (int v = t; v != s; ) {
        int i = pre[v];
        adj[i].c -= incf[t];
        adj[i ^ 1].c += incf[t];
        v = adj[i ^ 1].v;
    }
    c += dst[t] * incf[t];
    f += incf[t];
}

int EK() {
    int f = 0, c = 0;
    while(spfa())
        update(f, c); 
    return c;
}

int getp(int x, int y) {
    return x * 3 + y + 1;
}

class Solution {
public:
    int minimumMoves(vector<vector<int>>& grid) {
        init();
        for (int i = 0; i < 3; ++ i) 
            for (int j = 0; j < 3; ++ j) {
                if (grid[i][j] > 1) {
                    add(s, getp(i, j), grid[i][j] - 1, 0);
                    for (int ii = 0; ii < 3; ++ ii) 
                        for (int jj = 0; jj < 3; ++ jj) 
                            if (!grid[ii][jj])
                                add(getp(i, j), getp(ii, jj), 1, abs(i - ii) + abs(j - jj));
                }
                else if(grid[i][j] == 0)
                    add(getp(i, j), t, 1, 0);
            }
                
        return EK();
    }
};

  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

EQUINOX1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值