传送门:CCF201409-4 最优配餐
一道很神奇的BFS题(蒟蒻的眼光)。题意大概是有m家门店,分别点了ci份单的k位顾客以及d个障碍物,骑手每携带一份外卖走一个单位花费一元钱,问m家门店该怎么分配配送任务使得总费用最少,当然,只需要输出最少的总费用。
身为一个蒟蒻,最开始我对每家分店都进行了一次BFS,超时了,很难受。不知该如何是好的我按捺不住偷代码的欲望,不小心瞥到了“所有分店同时BFS”的字样,灵机一动,一个字母都还没偷成的我关掉了网页,开始重构代码,然后就AC了...
值得注意的是,一个坐标可能有多位顾客,所以在读入的时候,若该坐标已有订单,就把新增的订单数加到原有的订单数上,然后将这两位顾客视作一位,就方便许多了。
然后就是一个常用的小技巧,已访问过的点会在vis数组中标记,同样的,我们可以把分店与不可经过的点标记进vis数组,这样代码就会更加简洁与清晰。
#include <bits/stdc++.h>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 1e3+10;
struct node
{
int x, y, t;
node(int X, int Y, int T): x(X), y(Y), t(T) {}
};
int n, m, k, d, K;
int g[maxn][maxn], vis[maxn][maxn];
int dir[4][2] = {1, 0, -1, 0, 0, 1, 0, -1};
ll ans = 0; //会爆int,应该使用long long
queue <node> q;
void read()
{
cin >> n >> m >> k >> d;
for(int i = 0; i < m; ++i)
{
int x, y;
cin >> x >> y;
vis[x][y] = 1;
q.push(node(x, y, 0)); //把每个分店都预先放进队列中
//这样bfs的时候,它们可以共用一个vis数组
//可以节省大量时间,就避免了超时
}
for(int i = 0; i < k; ++i)
{
int x, y, c;
cin >> x >> y >> c;
if(g[x][y])
g[x][y] += c;
else
{
g[x][y] = c;
K++;
}
}
for(int i = 0; i < d; ++i)
{
int x, y;
cin >> x >> y;
vis[x][y] = 1;
}
}
bool isLegal(int X, int Y, int i)
{
int x = X+dir[i][0], y = Y+dir[i][1];
if(x < 1 || x > n || y < 1 || y > n)
return false;
if(vis[x][y])
return false;
return true;
}
void bfs()
{
while(!q.empty())
{
node cur = q.front();
q.pop();
if(g[cur.x][cur.y])
{
ans += cur.t*g[cur.x][cur.y];
if(!(--K)) //所有订单配送完毕
return;
}
for(int i = 0; i < 4; ++i)
if(isLegal(cur.x, cur.y, i))
{
q.push(node(cur.x+dir[i][0], cur.y+dir[i][1], cur.t+1));
vis[cur.x+dir[i][0]][cur.y+dir[i][1]] = 1;
}
}
}
void solve()
{
bfs();
cout << ans;
}
int main()
{
read();
solve();
return 0;
}