问题描述
栋栋最近开了一家餐饮连锁店,提供外卖服务。随着连锁店越来越多,怎么合理的给客户送餐成为了一个急需解决的问题。
栋栋的连锁店所在的区域可以看成是一个n×n的方格图(如下图所示),方格的格点上的位置上可能包含栋栋的分店(绿色标注)或者客户(蓝色标注),有一些格点是不能经过的(红色标注)。
方格图中的线表示可以行走的道路,相邻两个格点的距离为1。栋栋要送餐必须走可以行走的道路,而且不能经过红色标注的点。
送餐的主要成本体现在路上所花的时间,每一份餐每走一个单位的距离需要花费1块钱。每个客户的需求都可以由栋栋的任意分店配送,每个分店没有配送总量的限制。
现在你得到了栋栋的客户的需求,请问在最优的送餐方式下,送这些餐需要花费多大的成本。
输入格式
输入的第一行包含四个整数n, m, k, d,分别表示方格图的大小、栋栋的分店数量、客户的数量,以及不能经过的点的数量。
接下来m行,每行两个整数xi, yi,表示栋栋的一个分店在方格图中的横坐标和纵坐标。
接下来k行,每行三个整数xi, yi, ci,分别表示每个客户在方格图中的横坐标、纵坐标和订餐的量。(注意,可能有多个客户在方格图中的同一个位置)
接下来d行,每行两个整数,分别表示每个不能经过的点的横坐标和纵坐标。
输出格式
输出一个整数,表示最优送餐方式下所需要花费的成本。
样例输入
10 2 3 3
1 1
8 8
1 5 1
2 3 3
6 7 2
1 2
2 2
6 8
样例输出
29
评测用例规模与约定
前30%的评测用例满足:1<=n <=20。
前60%的评测用例满足:1<=n<=100。
所有评测用例都满足:1<=n<=1000,1<=m, k, d<=n^2。可能有多个客户在同一个格点上。每个客户的订餐量不超过1000,每个客户所需要的餐都能被送到。
栋栋最近开了一家餐饮连锁店,提供外卖服务。随着连锁店越来越多,怎么合理的给客户送餐成为了一个急需解决的问题。
栋栋的连锁店所在的区域可以看成是一个n×n的方格图(如下图所示),方格的格点上的位置上可能包含栋栋的分店(绿色标注)或者客户(蓝色标注),有一些格点是不能经过的(红色标注)。
方格图中的线表示可以行走的道路,相邻两个格点的距离为1。栋栋要送餐必须走可以行走的道路,而且不能经过红色标注的点。
送餐的主要成本体现在路上所花的时间,每一份餐每走一个单位的距离需要花费1块钱。每个客户的需求都可以由栋栋的任意分店配送,每个分店没有配送总量的限制。
现在你得到了栋栋的客户的需求,请问在最优的送餐方式下,送这些餐需要花费多大的成本。
输入格式
输入的第一行包含四个整数n, m, k, d,分别表示方格图的大小、栋栋的分店数量、客户的数量,以及不能经过的点的数量。
接下来m行,每行两个整数xi, yi,表示栋栋的一个分店在方格图中的横坐标和纵坐标。
接下来k行,每行三个整数xi, yi, ci,分别表示每个客户在方格图中的横坐标、纵坐标和订餐的量。(注意,可能有多个客户在方格图中的同一个位置)
接下来d行,每行两个整数,分别表示每个不能经过的点的横坐标和纵坐标。
输出格式
输出一个整数,表示最优送餐方式下所需要花费的成本。
样例输入
10 2 3 3
1 1
8 8
1 5 1
2 3 3
6 7 2
1 2
2 2
6 8
样例输出
29
评测用例规模与约定
前30%的评测用例满足:1<=n <=20。
前60%的评测用例满足:1<=n<=100。
所有评测用例都满足:1<=n<=1000,1<=m, k, d<=n^2。可能有多个客户在同一个格点上。每个客户的订餐量不超过1000,每个客户所需要的餐都能被送到。
问题分析——
思路一:
反过来想,则问题变成了订外卖的人去找最近的外卖店,利用广度优先搜索,找到离这个人最近的外卖店。
实现广度优先搜索用到了队,即先将一个点进队,并搜索其周围的四个点,如果符合相应的条件(既不是障碍也不是外卖店),就将这个点进队。
在下面的代码中,map表示地图,通过将vice_map[i][j]标记为1,代表map上的点[i][j]已经被访问。
#include<stdio.h>
#define N 1010
typedef struct node //x,y记录进队的点的坐标
{
int x, y;
int value; //value记录数值,用于判断进队的点是不是外卖店(value = -1)
int depth; //depth记录人到外卖店的距离
}Node;
int n, dep = 0, rear =-1, front = -1;
int map[N][N] = {0}, vice_map[N][N] = {0};
Node queue[N * N] = {0};
void search(int i, int j);
int main()
{
int shop, guest, obs; // obs: obstacle
int i, j, k, m, row, col;
int num_eat;
long long sum = 0;
// read the information
scanf("%d%d%d%d", &n, &shop, &guest, &obs);
for(i=0; i<shop; i++)
{
scanf("%d%d", &row, &col);
map[row][col] = -1; //外卖店用-1表示
}
for(i=0; i<guest; i++)
{
scanf("%d%d%d", &row, &col, &num_eat);
if(map[row][col] != 0)
map[row][col] += num_eat; //某个点的值代表所叫外卖的份数(>1)
else
map[row][col] = num_eat;
}
for(i=0; i<obs; i++)
{
scanf("%d%d", &row, &col);
map[row][col] = -2; //障碍用-2表示
}
for(i=1; i<=n; i++)
for(j=1; j<=n; j++)
{
front = rear = -1; //初始化
dep = 0;
if(map[i][j] > 0) //发现一个订外卖的点
{
for(k=1; k<=n; k++) //初始化vice_map
for(m=1; m<=n; m++)
vice_map[k][m] = 0;
queue[++rear].x = i; //将这个订外卖点的信息加入队列
queue[rear].y = j;
queue[rear].value = map[i][j];
queue[rear].depth = dep;
vice_map[i][j] = 1; //并在vice_map相对位置上进行标记
//广度优先算法,对一个点退队,并将其周围的符合条件的点加入队列
while(1) //发现商店就停止搜索,
{
dep = queue[front].depth + 1;
search(queue[front].x - 1, queue[front].y);
if(queue[rear].value == -1)
break;
search(queue[front].x, queue[front].y + 1);
if(queue[rear].value == -1)
break;
search(queue[front].x + 1, queue[front].y);
if(queue[rear].value == -1)
break;
search(queue[front].x, queue[front].y - 1);
if(queue[rear].value == -1)
break;
}
sum += map[i][j] * queue[front].depth; //计算所花的钱
}
}
printf("%I64d", sum);
return 0;
}
void search(int i, int j) //对于一个点,如果没有超出边界,且既不是障碍也没有被搜索过,则加入队列
{
if(i > 0 && i <= n && j> 0 && j <= n && map[i][j] != -2 && vice_map[i][j] != 1)
{
queue[++rear].x = i;
queue[rear].y = j;
queue[rear].value = map[i][j];
queue[rear].depth = dep;
vice_map[i][j] = 1; //标记map[i][j]点为已被搜索过
}
}
但是这个算法并不是很好,因为耗时太长——对于每一个人,都要在全图广度优先搜索一遍,如果人数比较多,地图有比较大,显然很耗时。(在平台提交之后,代码运行耗时1.1秒左右!)
所以请教大神们之后,有了下面的算法——
将全部的外卖店进队,并对全图进行广度优先搜索,将每个点到最近的外卖店的距离记录在vice_map的相应点中。这样的话只对全图进行了一次广度优先搜索,就可以知道每个点离最近的外卖店的距离,相对于之前的每个人对全图广度优先搜索一次,找到这个人到最近的外卖点的距离的算法快了不止一两点!
#include<stdio.h>
#define N 1010
typedef struct node
{
int x, y;
int depth;
}Node;
int n, dep = 0, rear =-1, front = -1;
int map[N][N] = {0}, vice_map[N][N] = {0};
Node queue[N * N] = {0};
void search_point(int i, int j, int dep);
void BFS();
int main()
{
int shop, guest, obs;
int i, j, row, col;
int num_eat;
long long sum = 0;
//读数据
scanf("%d%d%d%d", &n, &shop, &guest, &obs);
for(i=0; i<shop; i++)
{
scanf("%d%d", &row, &col);
map[row][col] = -1;
}
for(i=0; i<guest; i++)
{
scanf("%d%d%d", &row, &col, &num_eat);
if(map[row][col] != 0)
map[row][col] += num_eat;
else
map[row][col] = num_eat;
}
for(i=0; i<obs; i++)
{
scanf("%d%d", &row, &col);
map[row][col] = -2;
}
//将所有的外卖店进队
for(i=1; i<=n; i++)
for(j=1; j<=n; j++)
{
if(map[i][j] == -1)
{
queue[++rear].x = i;
queue[rear].y = j;
queue[rear].depth = 0;
vice_map[i][j] = 1;
}
}
BFS();
for(i=1; i<=n; i++)
for(j=1; j<=n; j++)
if(map[i][j] > 0)
sum += (long long)map[i][j] * vice_map[i][j];
printf("%I64d\n", sum);
return 0;
}
void BFS()
{
while(++front < rear) //将外卖店出对,并向外扩散,对全图的点进行搜索
{
search_point(queue[front].x - 1, queue[front].y, queue[front].depth);
search_point(queue[front].x, queue[front].y + 1, queue[front].depth);
search_point(queue[front].x + 1, queue[front].y, queue[front].depth);
search_point(queue[front].x, queue[front].y - 1, queue[front].depth);
}
}
void search_point(int i, int j, int dep)
{
if(i > 0 && i <= n && j> 0 && j <= n && map[i][j] != -2 && vice_map[i][j] == 0)
{
queue[++rear].x = i;
queue[rear].y = j;
queue[rear].depth = dep + 1;
vice_map[i][j] = queue[rear].depth; //vice_map[][]记录的是某个点到最近的商店的距离
}
}
这个算法才用时160ms左右,显然比上一个代码快了不止一两点!