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 }
View Code

 

  题目链接:

  https://codingcompetitions.withgoogle.com/kickstart/round/0000000000051061/0000000000161476

 

posted on 2019-08-19 21:40  askl123  阅读(201)  评论(0)    收藏  举报

导航