P7916 交通规划

题目描述

给定一个平面上 nn 条水平直线和 mm 条垂直直线,它们相交形成 nn 行 mm 列的网格,从上到下第 rr 条水平直线和从左到右第 cc 条垂直直线之间的交点称为格点 (r, c)(r,c)。网格中任意两个水平或垂直相邻的格点之间的线段称为一条边,每条边有一个非负整数边权。

进行 TT 次询问,每次询问形式如下:

给出 kk(TT 次询问的 kk 可能不同)个附加点,每个附加点位于一条从网格边缘向外出发的射线上。所有从网格边缘向外出发的射线按左上-右上-右下-左下-左上的顺序依次编号为 11 到 2 n + 2 m2n+2m,如下图:

对于每次询问,不同附加点所在的射线互不相同。每个附加点和最近的格点之间的线段也称为一条边,也有非负整数边权(注意,在角上的格点有可能和两个附加点同时相连)。

给定每个附加点的颜色(黑色或者白色),请你将网格内每个格点的颜色染成黑白二者之一,并使得所有两端颜色不同的边的边权和最小。请输出这个最小的边权和。

输入格式

第一行,三个正整数 n, m, Tn,m,T,分别表示水平、垂直直线的数量,以及询问次数。

接下来 n - 1n−1 行,每行 mm 个非负整数。其中第 ii 行的第 jj 个非负整数 {x 1}_{i, j}x1i,j​ 表示 (i, j)(i,j) 和 (i + 1, j)(i+1,j) 间的边权。

接下来 nn 行,每行 m - 1m−1 个非负整数。其中第 ii 行的第 jj 个非负整数 {x 2}_{i, j}x2i,j​ 表示 (i, j)(i,j) 和 (i, j + 1)(i,j+1) 间的边权。

接下来依次输入 TT 组询问。第 ii 组询问开头为一行一个正整数 k_iki​ 表示这次询问附加点的总数。接下来 k_iki​ 行每行三个非负整数。其中第 jj 行依次为 {x 3}_{i, j}, p_{i, j}, t_{i, j}x3i,j​,pi,j​,ti,j​ 表示第 jj 个附加点和相邻格点之间的边权、所在的射线编号以及附加点颜色(00 为白色,11 为黑色)。保证同一组询问内 p_{i, j}pi,j​ 互不相同。

每行的多个整数由空格分隔。

输出格式

输出 TT 行,第 ii 行输出一个非负整数,表示第 ii 次询问染色之后两端颜色不同的边权和的最小值。

输入输出样例

输入 #1复制

2 3 1
9 4 7
3 8
10 5
2
19 3 1
17 9 0

输出 #1复制

12

说明/提示

【样例解释 #1】

最优方案:(1, 3), (1, 2), (2, 3)(1,3),(1,2),(2,3) 为黑色;(1, 1), (2, 1), (2, 2)(1,1),(2,1),(2,2) 为白色。

【数据范围】

测试点编号n, m \len,m≤k_i \leki​≤
1 \sim 21∼2555050
3 \sim 53∼5181822
6 \sim 86∼818185050
9 \sim 109∼1010010022
11 \sim 1211∼121001005050
13 \sim 1613∼1650050022
17 \sim 2017∼205005005050

对于所有数据,2 \le n, m \le 5002≤n,m≤500,1 \le T \le 501≤T≤50,1 \le k_i \le \min \{ 2 (n + m), 50 \}1≤ki​≤min{2(n+m),50},1 \le \sum_{i = 1}^{T} k_i \le 501≤∑i=1T​ki​≤50,0 \le x \le {10}^60≤x≤106,1 \le p \le 2 (n + m)1≤p≤2(n+m),t \in \{ 0, 1 \}t∈{0,1}。

保证对于每个 i \in [1, T]i∈[1,T],p_{i, j}pi,j​ 互不相同。

上代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int INF = 0x3f3f3f3f;
const LL mod = 1e9 + 7;
const int N = 260000;

struct Edge {
    int v, w, next;
} e[N * 10];
int h[N], _h[N], eid;
void addEdge(int u, int v, int w) {
    e[eid] = {v, w, h[u]};
    h[u] = eid++;
}

int n, m, _;
int a[2005], c[2005];
vector<int> zoo;       //外面一圈的编号
int b[N], tot;         //tot表示块号 b表示外面一圈点属于的块号
vector<int> pig[100];  //每块的结点编号
int d[N], vis[N];
struct Node {
    int u, d;
    bool operator<(const Node &rhs) const {
        return d > rhs.d;
    }
};
int g[100][100], f[100][100];
void dijk(int s) {
    memset(d, 0x3f, sizeof d);
    memset(vis, 0, sizeof vis);
    priority_queue<Node> q;
    for (auto x : pig[s]) {
        d[x] = 0;
        q.push({x, 0});
    }
    int cnt = 0; // 无聊加了一个优化,不是很有必要,但是效果明显
    while (q.size() && cnt < tot) {
        int u = q.top().u;
        q.pop();
        if (vis[u]) continue;
        vis[u] = 1;
        if (b[u] > 0 && g[s][b[u]] == -1) g[s][b[u]] = d[u], cnt++; 
        for (int i = h[u]; ~i; i = e[i].next) {
            int v = e[i].v, w = e[i].w;
            if (d[v] > d[u] + w) {
                d[v] = d[u] + w;
                q.push({v, d[v]});
            }
        }
    }
}
int id(int x, int y) {
    return x * (m + 1) + y;
}
// 通过射线编号找顺时针下一个结点编号
int id2(int x) {
    if (x <= m) {
        return id(0, x);
    } else if (x <= m + n) {
        x -= m;
        return id(x, m);
    } else if (x <= m + n + m) {
        x -= m + n;
        x = m - x + 1;
        return id(n, x - 1);
    } else {
        x -= m + n + m;
        x = n - x + 1;
        return id(x - 1, 0);
    }
}
int main() {
    memset(h, -1, sizeof h);
    scanf("%d%d%d", &n, &m, &_);
    for (int i = 1; i < n; i++) {
        for (int j = 1; j <= m; j++) {
            int u = id(i, j - 1), v = id(i, j), w;
            scanf("%d", &w);
            addEdge(u, v, w);
            addEdge(v, u, w);
        }
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j < m; j++) {
            int u = id(i - 1, j), v = id(i, j), w;
            scanf("%d", &w);
            addEdge(u, v, w);
            addEdge(v, u, w);
        }
    }

    for (int u = 0; u < 2; u++) {
        for (int i = 0; i <= m; i++) zoo.push_back(id(0, i));
        for (int i = 1; i <= n - 1; i++) zoo.push_back(id(i, m));
        for (int i = m; i >= 0; i--) zoo.push_back(id(n, i));
        for (int i = n - 1; i >= 1; i--) zoo.push_back(id(i, 0));
    }

    memcpy(_h, h, sizeof h);

    while (_--) {
        memcpy(h, _h, sizeof h);
        memset(a, 0, sizeof a);
        memset(b, 0, sizeof b);
        memset(c, -1, sizeof c);
        memset(g, -1, sizeof g);
        for (int i = 1; i <= tot; i++) pig[i].clear();
        tot = 0;
        int TT;
        scanf("%d", &TT);
        int flag = 0;
        for (int i = 0; i < TT; i++) {
            int x, p, t;
            scanf("%d%d%d", &x, &p, &t);
            a[p] = x, c[p] = t;
            flag |= 1 << t;
        }
        if (flag != 3) {
            puts("0");
            continue;
        }
        for (int i = 1; i <= m; i++) {
            int u = id(0, i - 1), v = id(0, i);
            addEdge(u, v, a[i]);
            addEdge(v, u, a[i]);
        }
        for (int i = 1; i <= n; i++) {
            int u = id(i - 1, m), v = id(i, m);
            addEdge(u, v, a[i + m]);
            addEdge(v, u, a[i + m]);
        }
        for (int i = 1; i <= m; i++) {
            int u = id(n, m - i + 1), v = id(n, m - i);
            addEdge(u, v, a[i + m + n]);
            addEdge(v, u, a[i + m + n]);
        }
        for (int i = 1; i <= n; i++) {
            int u = id(n - i + 1, 0), v = id(n - i, 0);
            addEdge(u, v, a[i + m + n + m]);
            addEdge(v, u, a[i + m + n + m]);
        }
        for (int i = 1; i <= 2 * n + 2 * m; i++) {
            if (c[i] == -1) continue;
            for (int j = i + 1;; j++) {
                if (j > 2 * n + 2 * m) j = 1;
                if (c[j] == -1) continue;
                if (c[j] != c[i]) {
                    tot++;
                    int x = id2(i), y = id2(j);
                    int flag = 0;
                    for (auto u : zoo) {
                        if (u == x) flag = 1;
                        if (flag) {
                            if (u == y) break;
                            b[u] = tot;
                            pig[tot].push_back(u);
                        }
                    }
                }
                break;
            }
        }

        for (int i = 1; i <= tot; i++) {
            dijk(i);
        }
        for (int len = 2; len <= tot; len += 2) {
            for (int i = 1, j = i + len - 1; j <= tot; i++, j++) {
                f[i][j] = INF;
                for (int k = i + 1; k <= j; k += 2) {
                    f[i][j] = min(f[i][j], g[i][k] + f[i + 1][k - 1] + f[k + 1][j]);
                }
            }
        }
        printf("%d\n", f[1][tot]);
    }
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值