CCF 201409-4 最优配餐
问题
问题描述
栋栋最近开了一家餐饮连锁店,提供外卖服务。随着连锁店越来越多,怎么合理的给客户送餐成为了一个急需解决的问题。
栋栋的连锁店所在的区域可以看成是一个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,每个客户所需要的餐都能被送到。
问题分析
一看这道题是个地图题,想到了DFS和BFS,然后发现是求最优问题,果断尝试BFS,但觉得好像哪里不对劲。其实这道题就是一个BFS加了点small trick= =。
首先明确一点:不论一个位置有多少个顾客,这个位置所有顾客的需求总量一定是由“最近”的一家分店承担的(可以用反证法证明)。然后就是哪家分店“最近”的问题了,“最近”一定是所有分店中最近的,所以考虑从所有分店同时开始BFS,到达一个顾客点就完成这个点的需求,显然这是正确的。为了“同时”开始BFS,在开始将所有的分店加入到队列中去就行了,由于BFS的特性,虽然各分店开始的顺序与加入队列的顺序有关,但是每一轮(同一层)的搜索都是同步的,所以虽然不是严格意义上的“同时”,但是这是没问题的。所以说这道题的一个小trick就是在开始将多个起点放入队列。其他的尽管BFS= =。
不过,还有个20分的坑估计很多人会跳进去。注意评测用例规模:“1<=n<=1000,1<=m, k, d<=n^2。可能有多个客户在同一个格点上。每个客户的订餐量不超过1000,每个客户所需要的餐都能被送到。”,考虑一种容易考虑的情况(如下图)
若左下角(1,1)有唯一一家分店,右上角(1000,1000)有1000,000个顾客,每个顾客的订餐量为1000,无禁止通过的点。那么,从分店(1,1)走到顾客所在的点(1000,1000)需要2000步左右;然后1000,000个顾客,每人订餐量1000,总订餐量10^9,总共花费2000 * 10^9 = 2 * 10^12,显然这超出了int的范围,所以要使用long long类型,使用时注意与计算相关的变量也使用long long类型避免在默认类型转换时发生错误。如果程序其他部分编写没问题,把这个问题改正就能从80分改到100分了。
代码
#include<cstdio>
#include<queue>
using namespace std;
const int MAX_SIZE = 1000;
struct point_info{
int x,y;
long long step;
};
bool Legal(int x,int y,int n){
if(x<1||x>n||y<1||y>n)
return false;
return true;
}
long long customer_need[MAX_SIZE+1][MAX_SIZE+1] = {0LL};//记录一个点所有顾客的总需求
int customers[MAX_SIZE+1][MAX_SIZE+1] = {0};//记录同一点有几个顾客
int main(){
int n,m,k,d,x,y;
long long ans = 0,value;
queue<point_info> q;
bool ban[MAX_SIZE+1][MAX_SIZE+1] = {false};
point_info front,t;
const int move[][2] = {{0,1},{1,0},{0,-1},{-1,0}};
scanf("%d %d %d %d",&n,&m,&k,&d);
while(m--){
point_info start;
start.step = 0LL;
scanf("%d %d",&start.x,&start.y);
ban[start.x][start.y] = true;
q.push(start);
}
for(int i=0;i<k;i++){
scanf("%d %d %lld",&x,&y,&value);
customer_need[x][y] += value;
customers[x][y]++;
}
while(d--){
scanf("%d %d",&x,&y);
ban[x][y] = true;
}
while(!q.empty()){
front = q.front();
q.pop();
if(customer_need[front.x][front.y]!=0LL){
k -= customers[front.x][front.y];
ans += front.step * customer_need[front.x][front.y];
if(k==0)
break;
}
for(int i=0;i<4;i++){
x = front.x+move[i][0];
y = front.y+move[i][1];
if(Legal(x,y,n)&&!ban[x][y]){
t.x = x;
t.y = y;
t.step = front.step + 1;
ban[x][y] = true;
q.push(t);
}
}
}
printf("%lld\n",ans);
return 0;
}
推荐阅读