kick start 2019 round D T3题解
---恢复内容开始---
题目大意:共有N个房子,每个房子都有各自的坐标X[i],占据每个房子需要一定花费C[i]。现在需要选择K个房子作为仓库,1个房子作为商店(与题目不同,概念一样),由于仓库到房子之间存在距离 | Xi-Xj | ,所以想要使“占据K+1个房子以及每个仓库到商店的距离和”最小化,并输出该最小值。
数据范围:K<N<=1e5,C[i],X[i]<=1e9。
解题思路:比赛期间使用的是N*N*log(N)的时间复杂度,总体思路是先选定商店位置,再将其他房子按照“花费+距离”进行快排,选出前K优的房子作为仓库。理想的时间复杂度是N*log(K)的,在看过题解后着实佩服google的题……曾经有人分我说过google出的题要是你没做出来,你也是心服口服的,现在我真的意识到了。
改解法依旧将问题剖解,首先是一个老生常谈的问题:如果已经选好了K+1个房子,那么商店放在哪合适?这是以往的基础题了,最后的结论是房子位置的中位数处。有了这样的概念后,在枚举商店时,就可以确认商店左右(关于X的左右)两边各有K/2间仓库(可能出现奇偶问题,大家可以自己想想)。于是来到下一个子问题:如何获得一个商店左侧C[i]-X[i]最小的K/2个值的和呢(这句话大家也可以想想),又如何获得一个商店右侧C[i]+X[i]最小的K-K/2个值的和呢?
这两个问题是强关联的(我最开始没发现。。真是傻),可以利用最大堆解决,以左侧为例,先将以X排序好的房子#1~#K/2的C[i]-X[i]值加入最大堆,并记录当前最值的最大堆所有元素和。当对#K/2+1~#N的房子依次进行分析时,首先看当前房间的C[i]-X[i]值是否小于最大堆的top值,若小于,则更新最大堆(弹出top,压入c[i]-x[i])与元素和。
维护好suml[]以及sumr[]两数组后,就可以枚举商店位置,计算以此处为商店的租界最小值,再将每个商店的租界最小值取最小值,即可得到答案。
语义不清。。有空再回顾一下重写吧。真是好题。
最终代码:
1 #include <stdio.h> 2 #include <queue> 3 #include <algorithm> 4 using namespace std; 5 6 priority_queue<long long> qq; 7 struct ooo{ 8 int xx,cc; 9 }hou[100010]; 10 bool cmp(ooo a,ooo b){ 11 return a.xx<b.xx; 12 } 13 int x[100010],c[100010]; 14 long long suml[100010],sumr[100010]; 15 int mainn() 16 { 17 int k,n;scanf("%d%d",&k,&n); 18 for (int i=1;i<=n;i++) scanf("%d",&hou[i].xx); 19 for (int i=1;i<=n;i++) scanf("%d",&hou[i].cc); 20 sort(hou+1,hou+n+1,cmp); 21 for (int i=1;i<=n;i++) 22 { 23 x[i]=hou[i].xx;c[i]=hou[i].cc; 24 } 25 while (!qq.empty()) qq.pop(); 26 suml[0]=0; 27 for (int i=1;i<=n;i++) 28 { 29 if (k==1) 30 { 31 suml[i]=0; 32 continue; 33 } 34 if (i<=k/2) 35 { 36 suml[i]=suml[i-1]+c[i]-x[i]; 37 qq.push(c[i]-x[i]); 38 continue; 39 } 40 if( c[i]-x[i] < qq.top() ){ 41 suml[i]=suml[i-1]-qq.top(); 42 qq.pop(); 43 qq.push(c[i]-x[i]); 44 suml[i]+=(c[i]-x[i]); 45 } 46 else 47 suml[i]=suml[i-1]; 48 } 49 while (!qq.empty()) qq.pop(); 50 sumr[n+1]=0; 51 for (int i=n;i>0;i--) 52 { 53 if ( qq.size()<k-k/2 ) 54 { 55 sumr[i]=sumr[i+1]+c[i]+x[i]; 56 qq.push(c[i]+x[i]); 57 continue; 58 } 59 if (c[i]+x[i]<qq.top() ){ 60 sumr[i]=sumr[i+1]-qq.top(); 61 qq.pop(); 62 qq.push(c[i]+x[i]); 63 sumr[i]+=(c[i]+x[i]); 64 } 65 else 66 sumr[i]=sumr[i+1]; 67 } 68 long long ans=-1; 69 for (int i=k/2+1;i+k-k/2<=n;i++) 70 { 71 long long tmp=c[i]+suml[i-1]+sumr[i+1]; 72 if (k%2) tmp-=x[i]; 73 if (ans==-1 || ans>tmp) 74 ans=tmp; 75 } 76 printf(" %lld\n",ans); 77 //for (int i=1;i<=n;i++) 78 // printf("%lld %lld\n",suml[i],sumr[i]); 79 80 } 81 82 int main() 83 { 84 int T;scanf("%d",&T); 85 for (int i=1;i<=T;i++) 86 { 87 printf("Case #%d:",i); 88 mainn(); 89 } 90 }
题目链接:
https://codingcompetitions.withgoogle.com/kickstart/round/0000000000051061/0000000000161476
浙公网安备 33010602011771号