安吉D9-T4
- 1安吉D19 T1
- 2安吉D19 T3
- 3安吉D20 模考12总结
- 4安吉D17 T3
- 5安吉D17 T4
- 6安吉D19 模考11总结
- 7安吉D15 A
- 8安吉D16 T4
- 9安吉D17 T1
- 10安吉D17 模考10总结
- 11安吉D16 T3
- 12安吉D16 模考9总结
- 13安吉D13 模考8总结
- 14安吉D13-T2
- 15安吉D13-T3
- 16安吉D12-T3
- 17安吉D11-B
- 18安吉D11-C
- 19安吉D11-D
- 20安吉D12 模考7总结
- 21安吉D12-T2
- 22安吉D11-A
- 23安吉D10 模考6总结
- 24安吉D10-T3
- 25安吉D10-T4
- 26安吉D8-E
- 27安吉D9-T4本文
- 28安吉D8-A
- 29安吉D8-B
- 30安吉D8-C
- 31安吉D8-D
- 32安吉D9 模考5总结
- 33安吉D9-T2
- 34安吉D9-T3
- 35安吉D6-T4
- 36安吉D4-G
- 37安吉D5-T2
- 38安吉D6 模考4总结
- 39安吉D6-T1
- 40安吉D6-T3
- 41安吉-开营测试 T4
- 42安吉D5 模考3总结
- 43安吉D5-T3
- 44安吉D4-A
- 45安吉D4-B
- 46安吉D4-C
- 47安吉D4:容斥原理
- 48安吉Day4-D
- 49安吉D3 模考2总结
- 50安吉D3-T1
- 51安吉D3-T2
- 52安吉D3-T4
- 53安吉D2 模考1总结
- 54安吉D2-T2
- 55安吉D1-G
- 56安吉D1-L
- 57安吉D2-T4
原题呈现
P5100 足球 / Soccer
题目描述
你是 JOI 联赛中一所声名卓著的足球俱乐部的经理.
俱乐部有 名球员,编号为 .球员们每天都刻苦地进行训练,剑指联赛冠军.足球场可视为一个底为 米,高 米的长方形,底平行于东西方向,高平行于南北方向.如果某个点向北走 米,再向西走 米恰好到达球场的西北角,这个点可用坐标 来表示.
练习结束后,你要回收练习用的足球.开始回收时,所有球员都在足球场上,球员 位于 ,球在球员 脚下.你正和球员 一起站在 ,并准备回收球.球员们把球传到 时,你才会回收球.
你可以指挥球员,但某些操作会提升球员的疲劳度.一个球员不能同时进行多项操作. 你可以指挥控球的球员进行如下操作:
- 踢球.在东西南北四个方向中任选一个,并指定一个正整数 ,该球员将球朝指定方向踢出恰好 米.假定球滚动时可以穿过其他球员.该球员不会移动,且自动停止控球,疲劳度上升 .
- 运球.在东西南北四个方向中任选一个,该球员带球,朝指定方向移动 米.该球员仍然控球,疲劳度上升 .
- 停止控球.该球员的疲劳度不改变.
你可以指挥没有控球的球员进行如下操作:
- 移动.在东西南北四个方向中任选一个,该球员朝指定方向移动 米,疲劳度上升 .
- 控球.如果该球员所在的位置恰好有球,且没有其他球员控球,该球员才能控球.该球员的疲劳度不改变.
球员和球有可能跑出场外,一个位置上可能有多个球员. 一天的训练结束后,球员们非常疲惫.你想知道在回收球的过程中,所有球员上升的疲劳度之和的最小值.
输入格式
第一行有两个整数 ,用空格分隔. 第二行有三个整数 ,用空格分隔. 第三行有一个整数 . 在接下来的 行中,第 行 有两个整数 ,用空格分隔. 输入的所有数的含义见题目描述.
输出格式
一行,一个整数,表示在回收球的过程中,所有球员上升的疲劳度之和的最小值.
输入输出样例 #1
输入 #1
6 51 3 631 10 46 5输出 #1
26输入输出样例 #2
输入 #2
3 30 50 1020 03 3输出 #2
60输入输出样例 #3
输入 #3
4 30 15 1020 04 3输出 #3
45输入输出样例 #4
输入 #4
4 60 5 100063 14 63 03 04 00 4输出 #4
2020说明/提示
样例解释 1
在这组样例中,球场、球员、球处于如图所示的状态.图中,黑框空心圆圈表示球员,实心圆表示球,你在 .

最优解如下:
- 球员 把球向东踢出 米.疲劳度上升了 ,球移动到 .
- 球员 向南移动 米.疲劳度又上升了 .
- 球员 开始控球.
- 球员 向东运球 米.疲劳度又上升了 .
- 球员 把球向南踢出 米,疲劳度上升了 ,球移动到 .
此时,疲劳度之和为 .没有更好的方案.

样例解释 2
在最优解中,不需要踢球.
样例解释 4
注意这组样例中有多个球员在同一位置的情况.
数据范围与提示
对于 的数据,. 对于另外 的数据,. 对于所有数据, .
注意到 的范围达到了 ,且球员时时刻刻都在跑动,无法记录球员的状态.于是可以反过来思考:我们可以记录球的状态.由于 和 都小于 500,因此,可以对于每一个 满足 都视为图上的一个点,最多只有 250000 个点.
对于球,其有两大类状态,一是球所在的位置;二是球的运动状态,关于运动状态,有以下三类:
- 被人持有,记为
0. - 水平自主移动,记为
1. - 横向自主移动,记为
2.
依次对于这几种状态之间的转移进行考虑:
- 即某一个人向一个方向运球.其转移为:
- 当一个球被踢出去:
- 或 (被踢出去之后的)球自己移动一步:
- 有一个人接住了这个球.此时需要距离这个位置最近的球员跑过来接球.不难证明,若是一名球员接两次必定不优,因此,选择初始位置距离 最近的球员.这个可以使用多源 BFS 预处理,设该球员距离球的距离为 ,则转移方程为:
关于上述 中的 BFS 预处理,一般的,我们可以通过以下的步骤来进行处理.使用一个队列:
- 首先,将所有的球员所在的位置入队.
- 取出队首的球员位置,让这位球员(设为 )向前后左右都移动一格(如果这个格子已经别的球员移动到过,则不朝这个方向移动),记录落点(设为 ),则离点 最近的球员即为球员 .将点 加入队列,重复这一步,直到队列清空.
依照上面几个转移方程建边,再跑一遍 dijkstra 即可.
标程
#include <bits/stdc++.h>#define int long longusing namespace std;typedef pair<int, int> pii;const int N = 1e5 + 100;const int MAXPT = 5 * 500 * 500 + 100;int h, w, a, b, c, n, dist[MAXPT], vis[MAXPT];pii pos[N];queue<pair<int, pii>> bfs_q;
// [v, cost]vector<pii> g[MAXPT];int near[MAXPT];
struct Node { int v, d; friend bool operator<(const Node a, const Node b) { return a.d > b.d; // 小根堆 }};
int pt(int a, int x, int y) { return ((a) * (w + 1) * (h + 1) + (x) * (w + 1) + (y));}int pt(int x, int y) { return pt(0, x, y); }
void bfs_upd(int pl, int x, int y) { if (!near[pt(x, y)]) { near[pt(x, y)] = pl; bfs_q.push({pl, {x, y}}); }}void bfs() { while (!bfs_q.empty()) { auto top = bfs_q.front(); bfs_q.pop(); int pl = top.first, x = top.second.first, y = top.second.second; if (x + 1 <= h) bfs_upd(pl, x + 1, y); if (x - 1 >= 0) bfs_upd(pl, x - 1, y); if (y + 1 <= w) bfs_upd(pl, x, y + 1); if (y - 1 >= 0) bfs_upd(pl, x, y - 1); }}
int dijkstra(int t) { memset(dist, 0x3f, sizeof(dist)); memset(vis, 0, sizeof(vis)); priority_queue<Node> pq; dist[t] = 0; pq.push({t, dist[t]}); while (!pq.empty()) { int u = pq.top().v; pq.pop(); if (vis[u]) continue; vis[u] = true; for (int i = 0; i < g[u].size(); i++) { int v = g[u][i].first; int w = g[u][i].second; if (dist[v] > dist[u] + w) { dist[v] = dist[u] + w; pq.push({v, dist[v]}); } } } return dist[pt(0, pos[n].first, pos[n].second)];}
signed main() { cin >> h >> w; cin >> a >> b >> c; cin >> n; for (int i = 1; i <= n; i++) { int s, t; cin >> s >> t; pos[i] = {s, t}; bfs_q.push({i, pos[i]}); near[pt(s, t)] = i; } bfs(); for (int x = 0; x <= h; x++) { for (int y = 0; y <= w; y++) { if (x + 1 <= h) { g[pt(0, x, y)].push_back({pt(0, x + 1, y), c}); g[pt(1, x, y)].push_back({pt(1, x + 1, y), a}); g[pt(0, x, y)].push_back({pt(1, x, y), b}); } if (x - 1 >= 0) { g[pt(0, x, y)].push_back({pt(0, x - 1, y), c}); g[pt(1, x, y)].push_back({pt(1, x - 1, y), a}); g[pt(0, x, y)].push_back({pt(1, x, y), b}); } if (y + 1 <= w) { g[pt(0, x, y)].push_back({pt(0, x, y + 1), c}); g[pt(2, x, y)].push_back({pt(2, x, y + 1), a}); g[pt(0, x, y)].push_back({pt(2, x, y), b}); } if (y - 1 >= 0) { g[pt(0, x, y)].push_back({pt(0, x, y - 1), c}); g[pt(2, x, y)].push_back({pt(2, x, y - 1), a}); g[pt(0, x, y)].push_back({pt(2, x, y), b}); } int nPl = near[pt(x, y)]; int dist = abs(x - pos[nPl].first) + abs(y - pos[nPl].second); g[pt(1, x, y)].push_back({pt(0, x, y), c * dist}); g[pt(2, x, y)].push_back({pt(0, x, y), c * dist}); } } cout << dijkstra(pt(0, pos[1].first, pos[1].second)); return 0;}文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!


