欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

CCF 201409-4 最优配餐

程序员文章站 2024-03-17 21:17:46
...

问题

问题描述

  栋栋最近开了一家餐饮连锁店,提供外卖服务。随着连锁店越来越多,怎么合理的给客户送餐成为了一个急需解决的问题。
  栋栋的连锁店所在的区域可以看成是一个n×n的方格图(如下图所示),方格的格点上的位置上可能包含栋栋的分店(绿色标注)或者客户(蓝色标注),有一些格点是不能经过的(红色标注)。
  方格图中的线表示可以行走的道路,相邻两个格点的距离为1。栋栋要送餐必须走可以行走的道路,而且不能经过红色标注的点。

CCF 201409-4 最优配餐
  送餐的主要成本体现在路上所花的时间,每一份餐每走一个单位的距离需要花费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,每个客户所需要的餐都能被送到。”,考虑一种容易考虑的情况(如下图)

CCF 201409-4 最优配餐

若左下角(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;
}

 

相关标签: CCF