UVALive - 6434 —(思维题)

 

题意:给出了你由n个数组成的序列,让你将这个序列分为成m个集合,使得每一个集合的最大值减最小值的差相加最小。(如果某集合只有一个数字,则最大值减最小值为0) 。

 

思路:首先我们不难想到,最优的分配方法一定是先给这n个数排序,然后将排序好的数分成m段。因为和一个数相差最小的数,就是在有序序列中与它相邻的数。

然后,要使得每段中最大值减最小值尽量小,那就应该尽量在相差较大的数字之间分段,这样就相当于抛弃了这一段差值。

比如,1,2,4,5,9,10。这六个数两两之间的差值分别为1,2,1,4,1.所以,如果要分成两段,应该在哪里分,当然是5和9之间,因为在所有的差值中,5和9是相差最多的,若在此处分段,表示你舍弃了大小为4的一段差值,而我们原本的目的就是舍弃尽量多的差值,使得最终的差值尽量小。如果要分成三段,那就是在2和4之间分,5和9之间分。

所以这题的解法就出来了:提前算出所有数之间的差值。然后在差值最大的几个数之间分段。

这题算最终结果还有个简便方法。就拿上面的例子来说。如果不分段,那最大值减最小值是9;如果分成两段,那就是在5和9之间分,两段的差值之和 = (5-1)+(10-9)=(10-1)-(9-5)=5。也就是说,最终结果会等于一开始的差值减去你舍去的差值。

 

具体看代码:

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #include<string>
 5 #include<cmath>
 6 #include<algorithm>
 7 #include<stack>
 8 #include<climits>
 9 #include<queue>
10 #define eps 1e-7
11 #define ll long long
12 #define inf 0x3f3f3f3f
13 #define pi 3.141592653589793238462643383279
14 using namespace std;
15 
16 int cmp(int a,int b)
17 {
18     return a>b;
19 }
20 
21 int main()
22 {
23     int t,n,m,cnt,a,b,res[105],num[105];
24     cin>>t;
25     while(t--)
26     {
27         scanf("%d%d",&n,&m);
28         for(int i=0; i<n; ++i)
29             scanf("%d",&num[i]);
30             
31         sort(num,num+n); //先给所有的数排序 
32         for(int i=1; i<n; ++i) //计算两两之间的差值 
33             res[i-1] = num[i] - num[i-1];
34             
35         sort(res,res+n-1,cmp);//给差值按从大到小排序 
36         int ans = num[n-1] - num[0]; //计算初始的最大差值 
37         
38         for(int i=0; i<m-1; ++i) //减去分段是舍弃的差值 
39             ans -= res[i];
40         printf("Case #%d: %d\n",++cnt,ans);
41     }
42     return 0;
43 }

 

posted @ 2018-07-27 16:41  特务依昂  阅读(234)  评论(0编辑  收藏  举报