二分答案,离散化等

二分答案

最佳牛围栏

题目链接

解题思路

代码:

#include<stdio.h>
#include<iostream>
#include<math.h>
#include<algorithm>
using namespace std;
const int  N = 1e5 + 10;
const double eps = 1e-5;
double a[N],b[N],sum[N];
int n,f;
int main(){
    scanf("%d%d",&n,&f);
    for(int i = 1 ;i<=n;++ i)scanf("%lf",&a[i]);
    double L = -1e6 , r = 1e6;
    // 二分平均数
    while(L + eps < r ){
        double mid = (L + r)/2;
        // 这一段长度中的平均数是否大于 mid ,都减去mid 后就变为 这一段的和是否大于0
        for(int i = 1;i<= n ;++i) b[i] = a[i] - mid;
        // 求前缀和
        for(int i = 1;i<=n;++i) sum[i] = sum[i-1] + b[i];
        double ans = -1e10;
        double min_val = 1e10;
        for(int i = f;i<=n;++i){
            min_val = min(min_val,sum[i-f]);
            ans = max(ans,sum[i] - min_val);
        }
        if(ans >=0) L = mid; else r = mid; 
    }
    cout<<int(r*1000)<<endl;
    return  0;
}

离散化

电影

题目链接

解题思路:

这道题目是一道离散化的好题,因为我们这里的语言,是非常的分散,所以我们可以把他们汇聚在一起,也就是重新定义这些语言,重新标号1,2,3,…,n1,2,3,…,n。
这道题目统计方面,因为时间限制非常严格,所以动用了C++11的哈希级别map,

代码:

#include<iostream>
#include<stdio.h>
#include<map>
#include<algorithm>
using namespace std;

const int N = 2e5 + 10;

int n,m;
map<int,int>lg;
int a[N] ,b[N] ,ans[N];

int main() {
    scanf("%d",&n);
    for(int i= 1;i<= n ;++i){
        int x;
        scanf("%d",&x);
        lg[x]++;
    }
    scanf("%d",&m);
    for(int i=1;i<=m;++i)
        scanf("%d",&a[i]);
    for(int i=1;i<=m;++i)
        scanf("%d",&b[i]);
    int maxx = 0;
    for(int i=1;i<=m;++i)
        maxx = max(maxx,lg[a[i]]);  //得到会语言最多的人数
    int k = 0;
    for(int i=1;i<=m;++i)
        if(lg[a[i]] == maxx) // 通过这个人数去 找能开心里边电影的编号存到数组中
            ans[k++] = i;
    if(k == 1) // 如果只有一场,直接输出
        printf("%d\n",ans[0]);
    else{
        int res = 0,pos = 0;
        for(int i = 0;i<k;++i) // 否则在这几个电影中 ,找到交开心的电影编号,如果相同选后面的
            if(lg[b[ans[i]]] >= res){ 
                res = lg[b[ans[i]]];
                pos = ans[i];
            }
        printf("%d\n",pos);
    }
    
    return 0;
}


中位数

货仓选址

题目链接

解题思路;

排序+中位数
中位数有非常优秀的性质,比如说在这道题目中,每一个点到中位数的距离,都是满足全局的最有性,而不是局部最优性。
具体的来说,我们设在仓库左边的所有点,到仓库的距离之和为pp,右边的距离之和则为qq,那么我们就必须让p+qp+q的值尽量小。
当仓库向左移动的话,pp会减少xx,但是qq会增加n−xn−x,所以说当为仓库中位数的时候,p+qp+q最小。
还是同样的一句话,画图理解很重要。

首先看两个点的情况
在这里插入图片描述
类推到n
在这里插入图片描述

代码:

#include<stdio.h>
#include<algorithm>

using namespace std;

const int N = 1e5 + 10;
int n,a[N];
long long ans;

int main(){
    scanf("%d",&n);
    for(int i = 1; i <= n;++i)
        scanf("%d",&a[i]);
        sort(a+1,a+1+n);
    int pos ;
    if(n&1)
        pos = (n+1)>>1;
    else
        pos = n >>1;
    ans = 0;
    for(int i = 1;i<= n;++i)
        ans += abs(a[i] - a[pos]);
    printf("%lld\n",ans);
    return 0;
}

动态中位数

题目链接

解题思路:

技巧:

  • 大根堆与小根堆
 priority_queue<int> down;                               //大根堆
 priority_queue<int, vector<int>, greater<int>> up;      //小根堆
  • n个数中位置为奇数的个数与中间的位置。

统一可以写成:(n+1)>>1

代码:

#include<stdio.h>
#include<algorithm>
#include<vector>
#include<queue>

using namespace std;

int t,T,id,n;
// 以大根堆的顶部作为中位数
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&id,&n);
        printf("%d %d\n",id,(n+1)>>1);
        int cnt = 0;
        priority_queue<int> down;                               //大根堆
        priority_queue<int, vector<int>, greater<int>> up;      //小根堆
        for(int i=1;i<=n;++i){
            int x;
            scanf("%d",&x);
            // 大根堆为空,或者当前值小于等于中位数,插入大根堆
            if(down.empty() || x<= down.top()) down.push(x);
            else up.push(x);//否者插入小根堆
            // 因为是大根堆堆顶是中位数,正常情况下 大根堆数量应为 小根堆数量 + 1
            // 所以 如果 大于 up.size() + 1 ,将顶部放到小根堆上
            if(down.size() > up.size() + 1){up.push(down.top()),down.pop() ;}
            // 而小跟堆是小于1个,所以以 等于的话就算是多了
            if(up.size() > down.size()) { down.push(up.top()) , up.pop(); }
            
            if(i&1){
                printf("%d ",down.top());
                if(++cnt % 10 == 0)putchar('\n');
            }
        }
        if(cnt %10)
            putchar('\n');
        
    }
    return  0;
}

逆序对

定义
对于一个序列a,若
i < j   a n d   a [ i ] > a [ j ] , 则 称 a [ i ] 与 a [ j ] 构 成 逆 序 对 i<j\ and \ a[i] > a[j] ,则称 a[i] 与 a[j] 构成逆序对 i<j and a[i]>a[j],a[i]a[j]

利用归并排序求

代码:

// left = 1, right = n;
void msort(int left , int right){
    if(left == right)return ;
    int mid = (left  + right) >>1,i,j,k;
    msort(left,mid),msort(mid+1,right);
    k = left;
    for( i = left,j = mid+1;i<=mid && j<=right;){
        if(a[i]<=a[j])
            b[k++] = a[i++];
        else
            b[k++] = a[j++] , ans += mid - i + 1; // 表明后半部分有比前半部分小的
            // 其中的个数 就是 i 为前半部分在比较的,即是它大于后半部分
            // 个数就是 它 到中间位置的距离。
    }
    while(i<=mid)b[k++] = a[i++];
    while(j<=right) b[k++] = a[j++],ans += mid-i+1;
   // printf("ans %d \n",ans);
    for( i = left;i<=right;++i)
        a[i] = b[i];
}

奇数码问题

题目链接

这类题目有结论:

当n是奇数时

当且仅当两个局面下网格中的数依次写成一行 n*n-1 个元素的序列后,不考虑空格,逆序对的奇偶性相同就可以。

当n是偶数时

当且仅当两个局面下网格中的数依次写成一行 n*n-1 个元素的序列后,不考虑空格,“逆序对数之差” 和 " 两个局面下空格所在的行数之差" 奇偶性相同时可以。

代码:

#include<stdio.h>
#include<algorithm>
#include<cstring>
const int N = 3e5 + 10;

int n,a[N],b[N];
long long cnt1  , cnt , ans;

void msort(int left , int right){
    if(left == right)return ;
    int mid = (left  + right) >>1,i,j,k;
    msort(left,mid),msort(mid+1,right);
    k = left;
    for( i = left,j = mid+1;i<=mid && j<=right;){
        if(a[i]<=a[j])
            b[k++] = a[i++];
        else
            b[k++] = a[j++] , ans += mid - i + 1; // 表明后半部分有比前半部分小的
            // 其中的个数 就是 i 为前半部分在比较的,即是它大于后半部分
            // 个数就是 它 到中间位置的距离。
    }
    while(i<=mid)b[k++] = a[i++];
    while(j<=right) b[k++] = a[j++],ans += mid-i+1;
   // printf("ans %d \n",ans);
    for( i = left;i<=right;++i)
        a[i] = b[i];
}

int  main(){
    while(~scanf("%d",&n)){
        int ok = 0,x; // 不把0加进去
        for(int i=1;i<=n*n;++i)
           { 
               scanf("%d",&x);
               if(!x)
                ok = 1;
                else
                    a[i-ok] = x;
           }
        ans = 0 , memset(b,0,sizeof b);
        msort(1,n*n);
        cnt = ans , memset(b,0,sizeof b),ok = 0;
        memset(a,0,sizeof a); // 因为0在的位置可能不一样所以要清空
        for(int i=1;i<=n*n;++i)
           { 
               scanf("%d",&x);
               if(!x)
                ok = 1;
                else
                    a[i-ok] = x;
           }
        ans = 0,msort(1,n*n);
        //printf("%d %d\n",cnt,ans);
        if((cnt & 1) == (ans & 1) ) // 记得加括号 , & 运算 比 == 都小
            printf("TAK\n");
        else
            printf("NIE\n");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

落春只在无意间

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值