2022118总结-------排序

2022/1/18总结-------排序

AcWing 104. 货仓选址

在一条数轴上有 N 家商店,它们的坐标分别为 A1∼AN。

现在需要在数轴上建立一家货仓,每天清晨,从货仓到每家商店都要运送一车商品。

为了提高效率,求把货仓建在何处,可以使得货仓到每家商店的距离之和最小。

输入格式

第一行输入整数 N。

第二行 N 个整数 A1∼AN。

输出格式

输出一个整数,表示距离之和的最小值。

数据范围

1≤N≤100000
0≤Ai≤400000

输入样例:

4
6 2 9 1

输出样例:

12

思路:排序+中位数

中位数的性质,比如说在这道题目中,每一个点到中位数的距离,都是满足全局的最有性,而不是局部最优性。

具体的来说,我们设在仓库左边的所有点,到仓库的距离之和为p,右边的距离之和则为q,那么我们就必须让p+q的值尽量小。

当仓库向左移动的话,p会减少x,但是q会增加n−x,所以说当为仓库中位数的时候,p+q最小。

code

#include <iostream>
#include <algorithm>
using namespace std;
const int N=100100;
int a[N],n,i,ans,sum;
int main(){
    cin>>n;
    for (i=1;i<=n;i++)
        cin>>a[i];
    sort(a+1,a+1+n);
    int sm=a[n/2+1];
    for (i=1;i<=n;i++)
        ans=ans+abs(a[i]-sm);
    cout<<ans;
    return 0;
}

[NOIP2002 提高组] 均分纸牌

有n堆纸牌,编号分别为 1,2,…, n。每堆上有若干张,但纸牌总数必为n的倍数。可以在任一堆上取若干张纸牌,然后移动。

移牌规则为:在编号为1的堆上取的纸牌,只能移到编号为 2 的堆上;在编号为 n 的堆上取的纸牌,只能移到编号为n-1的堆上;其他堆上取的纸牌,可以移到相邻左边或右边的堆上。

现在要求找出一种移动方法,用最少的移动次数使每堆上纸牌数都一样多。

例如 n=4,4堆纸牌数分别为: ① 9  ② 8  ③ 17  ④ 6

移动3次可达到目的:

从 ③ 取4张牌放到④(9 8 13 10)->从③取3张牌放到 ②(9 11 10 10)-> 从②取1张牌放到①(10 10 10 10)。

输入

n(n 堆纸牌,1 ≤ n ≤ 100)

a1 a2 … an (n 堆纸牌,每堆纸牌初始数,l≤ ai ≤10000)。

输出

所有堆均达到相等时的最少移动次数。

输入样例

4
9 8 17 6

输出样例

3

思路

同类型经典题,求平均数,然后每个数减去平均数,然后不管是正数还是负数,都向下一个移动,最后差会都等于0。

code

#include <iostream>
using namespace std;
int main() {
    int a, p = 0, js = 0;
    cin >> a;
    int q[a];
    for (int i = 0; i < a; i++) {
        cin >> q[i];
        p += q[i];
    }
    p /= a;
    for (int i = 0; i < a; i++)q[i] -= p;
    for (int i = 0; i < a; i++) {
        if (q[i] == 0)continue;
        q[i + 1] += q[i];
        js++;
    }
    cout << js;
    return 0;
}

AcWing 122. 糖果传递

有 n 个小朋友坐成一圈,每人有 a[i] 个糖果。每人只能给左右两人传递糖果。每人每次传递一个糖果代价为 1。求使所有人获得均等糖果的最小代价。

输入格式

第一行输入一个正整数 n,表示小朋友的个数。

接下来 n 行,每行一个整数 a[i],表示第 i 个小朋友初始得到的糖果的颗数。

输出格式

输出一个整数,表示最小代价。

数据范围

1≤n≤1000000,0≤a[i]≤2×10^9. 数据保证一定有解。

输入样例:

4
1
2
5
4

输出样例:

4

思路

首先,最终每个小朋友的糖果数量可以计算出来,等于糖果总数除以n,用ave表示。

推公式:现有的+收到的-送出的=ave(最后的糖数)

让他们统一往一个方向送糖,则有child[i]=(i-1)ave-child[i]+child[i+1];

咱找此顺序从小到大排列就变成了 104. 货仓选址

code

#include<iostream>
 #include<algorithm>
 #include<cstring>
 typedef long long ll;
 using namespace std;
 int n; ll child[10000002],he[10000002];
 int main() {
   cin >> n;
   for (int i = 1; i <= n; i++){
     cin >> he[i];
     he[i]+=he[i-1];
   }
   ll ave=he[n]/n;
   for (int i = 2; i <= n; i ++ ) child[i] = (i - 1) * ave - (he[i] - he[1]);
   sort(child+1,child+1+n);
   ll mid=child[n+1>>1];
   ll res=0;
   for(int i=1;i<=n;i++)res+=abs(child[i]-mid);
   cout<<res;
   return 0;
 }//这题就是推公式

AcWing 105. 七夕祭

七夕节因牛郎织女的传说而被扣上了「情人节」的帽子。于是 TYVJ 今年举办了一次线下七夕祭。Vani 同学今年成功邀请到了 cl 同学陪他来共度七夕,于是他们决定去 TYVJ 七夕祭游玩。TYVJ 七夕祭和 11 区的夏祭的形式很像。矩形的祭典会场由 NN 排 MM 列共计 N×MN×M 个摊点组成。虽然摊点种类繁多,不过 cl 只对其中的一部分摊点感兴趣,比如章鱼烧、苹果糖、棉花糖、射的屋……什么的。Vani 预先联系了七夕祭的负责人 zhq,希望能够通过恰当地布置会场,使得各行中 cl 感兴趣的摊点数一样多,并且各列中 cl 感兴趣的摊点数也一样多。

不过 zhq 告诉 Vani,摊点已经随意布置完毕了,如果想满足 cl 的要求,唯一的调整方式就是交换两个相邻的摊点。两个摊点相邻,当且仅当他们处在同一行或者同一列的相邻位置上。由于 zhq 率领的 TYVJ 开发小组成功地扭曲了空间,每一行或每一列的第一个位置和最后一个位置也算作相邻。现在 Vani 想知道他的两个要求最多能满足多少个。在此前提下,至少需要交换多少次摊点。

输入格式

第一行包含三个整数 NN 和 MM 和 TT,TT 表示 cl 对多少个摊点感兴趣。

接下来 TT 行,每行两个整数 x,yx,y,表示 cl 对处在第 xx 行第 yy 列的摊点感兴趣。

输出格式

首先输出一个字符串。

如果能满足 Vani 的全部两个要求,输出 both;

如果通过调整只能使得各行中 cl 感兴趣的摊点数一样多,输出 row;

如果只能使各列中 cl 感兴趣的摊点数一样多,输出 column;

如果均不能满足,输出 impossible。

如果输出的字符串不是 impossible, 接下来输出最小交换次数,与字符串之间用一个空格隔开。

数据范围

1≤N,M≤1000001≤N,M≤100000,
0≤T≤min(N∗M,100000)0≤T≤min(N∗M,100000),
1≤x≤N1≤x≤N,
1≤y≤M1≤y≤M

输入样例:

2 3 4
1 3
2 1
2 2
2 3

输出样例:

row 1

思路:前缀和+排序+中位数+分治+贪心(分治是一种思想)

这道题目有一个非常重要的性质就是,只会改变相邻的两个数的位置,因此我们交换两个数,只会改变一行的喜爱小摊或者一列的喜爱小摊,而不会同时改变行和列的喜爱小摊,既然这样的话,我们就可以将这道题目分成两个部分,一部分是求行的最少次数,一部分是求列的最少次数。

那么这道题目就成为了环形的均分纸牌问题但是环形均分纸牌问题和普通均分纸牌问题又有不同之处,因此我们要截环为序列,所以说我们可以利用中位数把环形变成区间。即糖果传递问题的变种。

code

#include <iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 100100;
#define ll long long
ll n, m, t, i, j, k, s[N], a[N], x, y, ans1, ans2, r[N], c[N], rr, cc;
void init() {
    cin >> n >> m >> t;
    for (i = 1; i <= t; i++) {
        int x, y;
        cin >> x >> y;
        r[x]++, c[y]++, rr++, cc++;
    }
}
bool work1() {
    if (t % n != 0)	return 0;
    memset(s, 0, sizeof(s));
    rr /= n;
    for (i = 1; i <= n; i++)	r[i] -= rr, s[i] = s[i - 1] + r[i];
    sort(s + 1, s + 1 + n);
    int k = (1 + n) >> 1;
    for (i = 1; i <= n; i++)ans1 += abs(s[i] - s[k]);
    return 1;
}
bool work2() {
    if (t % m != 0)	return 0;
    memset(s, 0, sizeof(s));
    cc /= m;
    for (i = 1; i <= m; i++)c[i] -= cc,	s[i] = s[i - 1] + c[i];
    sort(s + 1, s + 1 + m);
    int k = (1 + m) >> 1;
    for (i = 1; i <= m; i++)ans2 += abs(s[i] - s[k]);
    return 1;
}
int main() {
    init();
    bool ok1 = work1(), ok2 = work2();
    if (ok1 && ok2)cout << "both";
    else if (ok1)cout << "row";
    else if (ok2)cout << "column";
    else cout << "impossible";
    if (ok1 || ok2)cout << " " << ans1 + ans2;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值