题目大意:
给出N个数,以及一个常数H,现在让我们将这个数组分成两个子部分,使得Max[F(ai,aj)]-Min[F(ai,aj)]的值最小。
这里F(ai,aj)=如果ai和aj在一个部分中:ai+aj,否则为:ai+aj+H
子部分可以为空
给出N个数,以及一个常数H,现在让我们将这个数组分成两个子部分,使得Max[F(ai,aj)]-Min[F(ai,aj)]的值最小。
这里F(ai,aj)=如果ai和aj在一个部分中:ai+aj,否则为:ai+aj+H
子部分可以为空
思路:
假设我们有一个长度为7的序列,此时我将其排序按照从大到小排序有:
A1 A2 A3 A4 A5 A6 A7
①我们挪动任意一个数A1~An5 之内的数都是只能使得结果maxn更大,而且并不能使得minn增大。
②当我们挪动两个数到另一个序列的时候也是同理。
③那么我们考虑只挪动最后两个数,当我们挪动倒数第二个数的时候
minn=min(A5+A7,A6+A7+H);显然是增大了minn的。
maxn=max(A1+A2,A1+A6+H);显然maxn是没有增大的,除非H很大。所以能够使得结果更优。
④如果我们挪动最后一个数的时候:
minn=min(A5+A6,A7+A6+H);显然A5+A6>=A5+A7。
maxn=max(A1+A2,A1+A7+H);显然A1+A7+H<=A1+A6+H;
所以挪动最后一个数肯定比挪动第一个数更好,所以我们这个问题一共有两种分配序列的方法得到最优:
1.一个空集合,另外一个就是序列本身
2.两个集合都非空,第一个集合只有序列中最小的元素min[ai],另外一个集合是剩下的所有元素
Ac代码:
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
using namespace std;
struct node
{
int val,pos;
}a[150000];
int cmp(node a,node b)
{
return a.val<b.val;
}
int main()
{
int n,h;
while(~scanf("%d%d",&n,&h))
{
for(int i=1;i<=n;i++)scanf("%d",&a[i].val),a[i].pos=i;
if(n==2)
{
printf("0\n");
printf("1 1\n");
continue;
}
sort(a+1,a+1+n,cmp);
int ans1=a[n].val+a[n-1].val-(a[1].val+a[2].val);
int ans2=max(a[n].val+a[1].val+h,a[n].val+a[n-1].val)-min(a[1].val+a[2].val+h,a[2].val+a[3].val);
printf("%d\n",min(ans1,ans2));
if(ans1<ans2)
{
for(int i=1;i<=n;i++)printf("1 ");
printf("\n");
}
else
{
for(int i=1;i<=n;i++)
{
if(i==a[1].pos)printf("2 ");
else printf("1 ");
}
printf("\n");
}
}
}