题目
【问题描述】
定义集合S的价值D(S)为:
现在给你n个元素,并给出其中任意两个元素之间的d(i,j)值,要你将这些元素划分成两个集合A、B。求min{D(A)+D(B)}。注:d(i,j)=d(j,i)。
【输入格式】
输入数据的第一行是一个整数n,代表元素个数。
之后n-1行描述的是d(i,j),这部分里,第i行包含n-i个整数,第i行第j列的整数代表的是d(i,i+j)。
【输出格式】
输出只有一行,一个整数,代表min{D(A)+D(B)}。
【输入样例】
【样例1】
5
4 5 0 2
1 3 7
2 0
4
【样例2】
7
1 10 5 5 5 5
5 10 5 5 5
100 100 5 5
10 5 5
98 99
3
【输出样例】
【样例1】
4
【样例2】
15
【数据范围】
1 ≤ n ≤ 200
0<=d(i,i+j)<=10^9
分析
这道题目我们首先可以做到一个判定性问题,也就是给定A集合的最大元素x1,和B集合的最大元素x2,我们可以判定我们能否得到这样的两个集合,因此顺理成章的就有了枚举x1然后二分猜x2的情况,这样要跑几分钟(如果动态维护要快一些,可以几秒跑完,但是少主很懒唉)
后来少主发现我们可以用单调指针完成,因为两边都有单调性了,这样就差不多4秒可以跑完,但是少主家的题库每组数据1S,很伤呀
后来少主又尝试了二分、三分,虽然这个并没有单调性,但是可以过很多组数据,(加起来可以过完的哦,但是少主是不愿意这么做的)
在将少主这个很牛X的方法之前,我们先前通过枚举+二分猜得到了一个很有力的工具,也就是一旦确定了x1,我们就可以确定x2的最小值,那么少主就想能不能也让x1跳得快一点呢,由此便有了以下算法
步骤(注意,所有的A,B都是建立在两个集合的可行解上面的,也就是所有出现过的值,还要手动加上0,也是作为二分答案的一个很好的剪枝,并且对应答案也一定是可行解)
1、确定边界A=1,并且通过二分确定边界最小B,记录一次答案
2、让B减1,确定出对应的A的最小值(肯定不比原来小,而且是对应B的最小可行解),然后再确定新的B(肯定比原来小,答案比确定新的B值之前肯定好些),记录下此时A、B对应的答案。
3、重复步骤2,直到B=1,但是为了不重复,可以加上剪枝,也就是A>=B的时候直接退出即可
这个算法并不是真正意义上的二分,只是利用了二分,实际上应该算是大步跳跃的一种,从步骤2我们大概可以看出一次更新AB之后,新的值肯定比与旧的AB之间的取值要好些,因此我们就可以大概确定我们得到了所有不