ICPC Mid-Central USA Region 2016 I题 Postal Delivery总结

ICPC Mid-Central USA Region 2016 I题 Postal Delivery总结

题目链接

https://nanti.jisuanke.com/t/40896

解题思路

我们可以将这题简化的看成这样一个问题:有一个x坐标轴,你现在位于原点0,在你的两旁,分别有若干堆信件,这些信件的位置为 x i x_i xi ( x i x_i xi 为正,则信件在坐标轴的正向;为负,则在坐标轴的反方向),每堆信件都有一定的数量 t i t_i ti
你要做的就是从原点出发,把所有的信件都搬到原点(题目是送信,不过都一样啦,都是要满足这些点的需求),当然,你一次最多只能搬 k k k 封信件,问搬完所有的信件走的走的最少路程。
对于原点两边的信件,我们的选择肯定是分开处理,就是说搬x轴正向的信件时不会经过x轴反方向。所以,我们把输入的数据分别存在两个数组里,分别处理这两个数组,当然处理方法是一样的。
存储数据用到结构体:

struct node{
	//a 距离 
	//b 信封数量
	int a, b;
};

数组定义如下:

//f 左边的信封
//z 右边的信封
node f[1050], z[1050];

我们以x轴正向为例。假设有 zn 堆信封,用 z[] 存右边信封的数据,我们来想一下,在最远一堆信封前面的信封都有可能在运后面的信封的过程中顺带带走,但是最后一堆信封不能这样,如果 z[zn-1].b(最后一堆信封的数量) 不是 k k k 的倍数,那么我们在运完 z[zn-1].b / k 趟后,还要再跑一趟把剩余的信封运走,这时如果只运走这些剩余的信封,我们还有余力运更多的信封,那么我们为何不好好利用这一趟呢?与其只运走剩余的的信封,不如沿途中顺带带走其它堆的信封,那么如何是最优的呢?最优的情况是:运走距离最后一堆最近的几堆信封。这样从后往前不断地贪心即可解决问题。
x轴负方向和正向一样。
源代码如下:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ll long long
using namespace std;

struct node{
	int a, b;
};

bool cmp(node a, node b) {
	return a.a < b.a;
}

//贪心求解
ll solve(node fz[], int k, int n) {
	ll res = 0;
	for (int i = n-1; i >= 0; --i) {
		//fz[i].b%k  搬第i堆最后一趟剩余的信封
		//diff       需要从前面的信封堆中减去的信封数量
		int diff = k-fz[i].b%k;
		if (diff == k) {
			res += fz[i].b / k * fz[i].a;
		} 
		else {
			res += (fz[i].b / k + 1) * fz[i].a;
			//从前面的信封堆中减去diff个信封
			for (int j = i-1; j >= 0 && diff; --j) {
				if (fz[j].b <= diff) {
					diff -= fz[j].b;
					fz[j].b = 0;
				}
				else {
					fz[j].b -= diff;
					diff = 0;
				}
			}
		}
	}
	return res;
}

int main(int argc, char** argv) {
	//f 左边的信封
	//z 右边的信封
	node f[1050], z[1050];
	int n, k;
	//fn  x轴负方向信封数量
	//zn  x轴正向信封数量
	int fn = 0, zn = 0;
	cin >> n >> k;
	int a, b;
	for (int i = 0; i < n; ++i) {
		cin >> a >> b;
		if (a < 0) {
			//把负的数转换成正数
			f[fn].a   = -a;
			f[fn++].b = b;
		}
		else {
			z[zn].a   = a;
			z[zn++].b = b;
		}
	}
	f[fn].b = 0;
	z[zn].b = 0;
	
	//因为要从后往前贪心,因此排序是必要的
	sort(f, f+fn, cmp);
	sort(z, z+zn, cmp);
	//左右分开处理
	ll res = solve(f, k, fn);
	res += solve(z, k, zn);
	//因为计算的是去的路程,回来的路程没算,所以要乘2
	printf("%lld\n", res*2);
	
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值