Fair Cut

from hackerrank
Li 和 Lu 两个人分整数,他们想分的尽量公平。公平度量为:
![](http://www.forkosh.com/mathtex.cgi? f(I) = \sum_ {i \in I}\sum_{j \in J}|a_i - a_j|)
其中![](http://www.forkosh.com/mathtex.cgi? I={i_1,i_2,...,i_k}, J = {1,...,n} \backslash I)
Li得到其中k个数,Lu得到剩下的数。

Input Format

第一行输入n (整数的数量) 和 k (Li得到的整数个数). 第二行输入n个整数,空格隔开。

Constraints
![](http://www.forkosh.com/mathtex.cgi?1 \leq k < n \leq 3000\1 \leq a_i \leq 10^9\15 % test case n \leq 20\45% test case n \leq 40)

Output Format

打印最小值的f

Sample Input 0

4 2
4 3 1 2

Sample Output 0

6

Explanation 0
![](http://www.forkosh.com/mathtex.cgi? I={2,4}, J = {1,3})
Sample Input 1

4 1
3 3 3 1

Sample Output 1

2

Explanation 1
![](http://www.forkosh.com/mathtex.cgi? I={1}, J = {2,3,4})

动规解

首先将数字排序,而后做一个背包问题:
![](http://www.forkosh.com/mathtex.cgi? dp[\alpha][\beta][\gamma] = min. \sum_ {i \in I}\sum_{j \in J}|a_i - a_j|;\ I=\beta,I \subset {1,...,\alpha};\J = {1,...,\alpha}\backslash I;\\sum_{i \in I} a_i = \gamma.)
每一步都有两个选择,将数字分给Li和Lu.
第一种情况,
第二种情况,
解就是:


be equal to (every number in is less or equal than , so we know how to resolve absolute value). In the second case we will get to the state and new function will be . The answer for the problem is .
This approach gives us pseudo-polynomial solution but it's not enough for the problem. In the first approach we noticed that information about sum of Li's numbers is enough to recalculate the function. Also it's clear that we express function for the new state as linear combination of function for the old state and sum of Li's numbers in the old state. This thought pushes us to extend dp state in the another way: minimal possible . To calculate we should consider two cases: if the last integer is Li's and if it's Lu's. In the first case intended value is
(similarly to the first approach). In the second case it is . Answer for the problem is . To obtain solution we should notice that with fixed and we have only one interesting (because in the end we are interested only in the state and changing of depends on changing of ).

#include 

using namespace std;

typedef long long ll;
typedef long double ld;
typedef pair pii;
#define mp make_pair
#define pb push_back
#define sz(x) ((int) (x).size())


#define X first
#define Y second

const int maxn = 3005;
ll a[maxn];
ll dp[2][maxn]; // minimal possible (unfairness - coef * (sum of Li's integers))

int main()
{
    int n, k;
    cin >> n >> k;
    for (int i = 0; i < n; i++)
        cin >> a[i];
    sort(a, a + n);
    for (int i = 0; i <= 1; i++)
       for (int j = 0; j <= k; j++)

              dp[i][j] = (i==0 && j==0?0ll:(ll)1e18);
    ll psum = 0ll;
    int coef;
    for (int i = 1; i <= n; i++)
    {
       for (int j = 0; j <= k; j++)
         {
            coef = (k-j)*2 - (n-i);
            dp[i&1][j] = min(dp[(i-1)&1][j] + (ll)j * a[i-1],  //Lu's
              (j>0 ? dp[(i-1)&1][j-1] - psum + (ll)coef * a[i-1] + (ll)(i-j) * a[i-1] : (ll)1e18));

         }
       psum += a[i-1];
    }
    cout << dp[n&1][k] << endl;
    return 0;
}

贪心解【from Discussion】

根据物理的“平衡定律law of equilibium”, 这道题相当于求一个电势平衡。
The optimal cut is independent of the numbers, but only depends on their order. For example, if there are N=10 numbers to cut, and one person gets K=3 numbers, the best cut will be 0001010100, which means we first sort the 10 numbers in ascending order A[1]<=A[2]<=...<=A[10], and give the 0s to one person and the 1s to another. The strategy is to place the 01-string repetition (the "010101" in the example) as close to the middle of the whole string as possible. If K*2>N, redefine K to be N-K (switching the 0s and 1s). The intuition behind this greedy strategy comes from physics. The unfairness is like an interaction potential to be minimized. The 0s and 1s attract each other and form an ionic crystal in the middle of a positive charge background.

n, k = map(int, raw_input().split(' '))
l = map(int, raw_input().split(' '))
l.sort()
if k > n / 2:
    k = n - k
t = (n-2*k) / 2
if (n - 2*k)%2 == 0:
    dig_str = '0'* t + '01' * k + '0' * t
else:
    dig_str = '0'* (t + 1) + '10' * k + '0' * t
I = [i for i in range(n) if dig_str[i] == '1']
J = [i for i in range(n) if dig_str[i] == '0']
print sum([abs(l[i]-l[j]) for i in I for j in J])

你可能感兴趣的:(Fair Cut)