题目地址:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=4096
题意
多组输入。
有n封信要送到指定地点,每次最多拿k封,重新拿信要回到位置0,求把所有信送完走的最小的距离
解题思路
贪心。
分x<0和x>0两部分,计算只去不回的距离(来回的距离*2即可)。
在坐标轴上从远往近(相对于位置0来说)贪心,每次都尽可能多的送信,用p记录每一段送信的最远距离,ans+=p。
最后取min(ans*2-左侧最远点,ans*2-右侧最远点),最后一次走的最远,且只去不回。
一定要从远往近贪心!!这样走的距离才最小!对最后一段送信的区间要特殊考虑下。
![](https://i-blog.csdnimg.cn/blog_migrate/8b322166099f056f29cedc27f138e607.png)
好吧,我讲不清楚了(´・Д・)」具体看代码吧
ac代码
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <ctype.h>
#include <set>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <sstream>
#define maxn 100005
typedef long long ll;
using namespace std;
struct node{
ll pos,num;//位置pos,要送num封信
friend bool operator <(node a,node b)
{
return a.pos>b.pos;
}
}l[maxn],r[maxn];
ll t,n,k,x;
ll half(node a[],ll numi)
{
ll res=0,sum=0,p=0;//记录每一段的起始位置
if(numi>=1) p=a[0].pos;
for(ll i=0;i<numi;)
{
sum=0;
while(i<numi)
{
sum+=a[i].num;
if(sum==k)
{
res+=p;
i++;
if(i<numi)
p=a[i].pos;
else p=0;
break;
}
else if(sum>k)
{
res+=p;
a[i].num-=(k-(sum-a[i].num));
p=a[i].pos;
break;
}
else //sum<k
i++;
}
}
if(p!=0)//最后一段的路程还没有记录到结果中
res+=p;
//cout<<"res"<<res<<endl;
return res;
}
int main()
{
//freopen("/Users/zhangkanqi/Desktop/11.txt","r",stdin);
scanf("%lld",&t);
while(t--)
{
memset(l,0,sizeof(l));
memset(r,0,sizeof(r));
ll ans = 0, numl = 0, numr = 0;
scanf("%lld %lld", &n, &k);
for (ll i = 0; i < n; i++)
{
scanf("%lld", &x);
if (x > 0)
{
r[numr].pos = x;
r[numr++].num++;
}
else if (x < 0)
{
l[numl].pos = -x;
l[numl++].num++;
}
}
sort(l, l + numl);
sort(r, r + numr);
ans+=half(l,numl);
ans+=half(r,numr);
printf("%lld\n",min(ans*2-l[0].pos,ans*2-r[0].pos));
}
return 0;
}