绝对值不等式


acwing 104.货仓选址

https://www.acwing.com/problem/content/106/

 

 题意:需要在竖轴上选一个点,使该点到其他给出点的距离之和最小。将其抽象为数学公式$\left | x - c_1 \right |+\left | x-c_2 \right |+\left | x-c_3 \right |+\cdot \cdot \cdot \left | x-c_k \right |$,求该公式的最小值。

可以知道,该公式的最小值就是取$c_1, c_2, c_3,\cdot \cdot \cdot c_k$的中位数。

证明:

当只有一个点的时候,选在该点肯定为最小,当有两个点的时候,选在两个点中间的任何一个位置都为最小,当有三个点的时候,选在中间那个点的位置。我们将每个点两两分组。

①当$k$为奇数时,$x_1$和$x_k$一组,$x_2$和$x_{k -1}$一组,最后剩$x_{\frac{k + 1}{2}}$单独一组,可以知道,选择每一组中间的点都是最小的。而$x_{\frac{k + 1}{2}}$这一组要选在这个点上最小,所以选在$x_{\frac{k + 1}{2}}$上的时候,满足该组的值最小,也满足其他组的值最小,所以总和最小。

②当$k$为偶数时,每一组都是两两配对,选择每组中间的位置就最小值,而最靠近中心的两个点的那个区间,选择任意一个在该区间内的点,都可以做到使得每一组的答案最小。

思路:读入一个$a$数组,排序,找出中位数,用所有数减去中位数的绝对值相加即为答案。

 1 #include <iostream>
 2 #include <algorithm>
 3 #include <cstring>
 4 
 5 using namespace std;
 6 
 7 const int N = 1e5+10;
 8 int a[N];
 9 int n;
10 
11 int main(){
12     cin >> n;
13     for(int i = 0 ; i < n ; i ++)cin >> a[i];
14     
15     sort(a, a + n);
16     int c = a[n / 2];
17     
18     int res = 0;
19     for(int i = 0 ; i < n ; i ++)res += abs(a[i] - c);
20     
21     cout << res << endl;
22     return 0;
23 }

 

 

acwing 122.糖果传递

https://www.acwing.com/problem/content/description/124/

 

 假设只向前方进行传递,设a_1传递的个数为x_1, 画出如图:

根据图,我们可以知道我们要求的就是公式$\left |x_1  \right |+\left | x_2 \right |+\cdot \cdot \cdot \left | x_n \right |$的最小值,而且我们可以写出一组公式

$\left\{\begin{matrix}a_1-x_1+x_2 = \bar{a}
& & & & \\ a_2-x_2+x_3=\bar{a}
& & & & \\ a_3-x_3+x_4=\bar{a}
& & & & \\ \cdot \cdot \cdot
& & & & \\a_n-x_n+x_1=\bar{a}
\end{matrix}\right.$

化简该公式:

$\left\{\begin{matrix}x_1=x_1 - 0
& & & \\ x_2=x_1-(a_1-\bar{a})
& & & \\ x_3=x_1-(a_1+a_2-2 \bar{a})
& & & \\ x_4 = x_1-(a_1+a_2+a_3 - 3\bar{a})
& & & \\ \cdot \cdot \cdot
& & & \\x_n=x_1-(a_1+a_2+\cdot \cdot \cdot a_{n-1}-(n-1)\bar{a})
\end{matrix}\right.$

可以看出,$x_1$后方的值可以看做常数项,将其设为$c$,

可以将公式再次化为:

$\left\{\begin{matrix}x_1=x_1 -c_1
& & & \\ x_2=x_1-c_2
& & & \\ x_3=x_1-c_3
& & & \\ x_4 = x_1-c_4
& & & \\ \cdot \cdot \cdot
& & & \\x_n=x_1-c_n
\end{matrix}\right.$

这样,题目就从求$\left |x_1  \right |+\left | x_2 \right |+\cdot \cdot \cdot \left | x_n \right |$的最小值,变成了求$\left |x_1 -c_1 \right |+\left | x_1-c_2 \right |+\cdot \cdot \cdot \left | x_1-c_n \right |$的最小值。转换为了求绝对值不等式的最小值。

思路:用递推的方式将数组$c$处理出,然后排序。循环与中位数相减将绝对值相加即为答案。

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <iostream>
 4 #include <algorithm>
 5 
 6 using namespace std;
 7 
 8 typedef long long LL;
 9 
10 const int N = 1000010;
11 
12 int n;
13 int a[N];
14 LL c[N];
15 
16 int main()
17 {
18     scanf("%d", &n);
19     for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);
20 
21     LL sum = 0;
22     for (int i = 1; i <= n; i ++ ) sum += a[i];
23 
24     LL avg = sum / n;
25     for (int i = n; i > 1; i -- )
26     {
27         c[i] = c[i + 1] + avg - a[i];
28     }
29     c[1] = 0;
30 
31     sort(c + 1, c + n + 1);
32 
33     LL res = 0;
34     for (int i = 1; i <= n; i ++ ) res += abs(c[i] - c[(n + 1) / 2]);
35 
36     printf("%lld\n", res);
37 
38     return 0;
39 }

 

acwing 105.七夕祭

https://www.acwing.com/problem/content/107/

 

 分析题意知道,这道题目有一个性质是,只会改变相邻的两个数的位置,因此我们交换两个数,只会改变一行的喜爱小摊或者一列的喜爱小摊,而不会同时改变行和列的喜爱小摊,这样,我们就可以将这道题目分成两个部分,一部分是求行的最少次数,一部分是求列的最少次数。这样原问题就从二维的问题拆成了一维的问题,等于在竖直方向上和水平方向上做糖果传递。

 1 #include <iostream>
 2 #include <algorithm>
 3 #include <cstring>
 4 
 5 using namespace std;
 6 
 7 typedef long long LL;
 8 
 9 const int N = 1000010;
10 
11 int n, m, T;
12 int col[N], row[N];
13 int s[N], c[N];
14 
15 LL solve(int n, int r[])
16 {
17     memset(s, 0, sizeof s);
18     memset(c, 0 ,sizeof c);
19     
20     for(int i = 1 ; i <= n ; i ++)s[i] = s[i - 1] + r[i];
21     
22     if(s[n] % n)return -1;
23     
24     int avg = s[n] / n;
25     
26     for(int i = 2 ; i <= n ; i ++)c[i] = s[i - 1] - (i - 1) * avg;
27     
28     sort(c + 1, c + n + 1);
29     
30     LL sum = 0;
31     for(int i = 1 ;  i <= n ; i ++)sum += abs(c[i] - c[(n + 1) / 2]);
32     
33     return sum;
34 }
35 
36 int main(){
37     
38     cin >> n >> m >> T;
39     
40     while(T --)
41     {
42         int x, y;
43         cin >> x >> y;
44         row[x] ++, col[y] ++;
45     }
46     
47     LL one = solve(n, row);
48     LL two = solve(m, col);
49     
50     if(one != -1 && two != -1)cout << "both" << " " << one + two << endl;
51     else if(one != -1)cout << "row" << " " << one;
52     else if(two != -1)cout << "column" << " " << two;
53     else cout << "impossible" << endl;
54     
55     return 0;
56 }

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM