题解 | #合唱队#

合唱队

http://www.nowcoder.com/practice/6d9d69e3898f45169a441632b325c7b4

/*最长上升子序列问题
中间最高,向两边逐渐减小(相等也不行)。不要求最高的同学左右人数必须相等。
不允许改变队列元素的先后顺序,也就是说,只能剔除不能排序。
计算最少需要出列几名同学满足以上要求,也就是说,剔除某些同学,剩下的队列自然而然的满足要求

(1)计算出每个同学左边最多有几个人满足从左到右依次增大的序列要求(包括自己)。
示例:186 186 150 200 160 130 197 200
      1   1   1   2   2   1   3   4
动态方程:
(2)计算出每个同学右边最多有几个人满足从右到左依次增大的序列要求(包括自己)。
示例:186 186 150 200 160 130 197 200
      3   3   2   3   2   1   1   1
动态方程:
(3)将左右最多序列人数相加减一(自己加了两遍),就得到以该数为中心时,所在队列最多人数。
然后依次算出所有的队列的最多人数,然后与总人数相减,其中最小值就是最少剔除人数。
总人数-该数所在队列人数=需要出队的人数
*/
#include <stdio.h>
#include <string.h>
int main(){
    int n, row[3000], Max;
    char lmax[3000],rmax[3000];
    while(scanf("%d",&n) != EOF){
        //初始化
        memset(lmax,1,3000);
        memset(rmax,1,3000);    //初始就自己一人,所以初始值为1
        Max = 0;                //记录最大序列人数
        //读数据
        for(int i = 0; i < n; i++){
            scanf("%d",&row[i]);
        }
        //计算各数左侧最长上升序列
        for(int i = 1; i < n; i++){
            for(int j = 0; j < i; j++){
                if(row[j] < row[i] && lmax[i] < lmax[j] + 1){
                    lmax[i] = lmax[j] + 1;
                }
            }
        }
        //计算各数右侧最长上升序列
        for(int i = n - 2; i >= 0; i--){
            for(int j = n - 1; j > i; j--){
                if(row[j] < row[i] && rmax[i] < rmax[j] + 1){
                    rmax[i] = rmax[j] + 1;
                }
            }
        }
        //计算最少剔除人数 = n - (lmax[i] + rmax[i] - 1)
        for(int i = 0; i < n; i++){
            int tmp =lmax[i] + rmax[i] - 1;
            if(Max < tmp) Max = tmp;
        }
        printf("%d\n",n - Max);
    }
    return 0;
}
/*最长上升子序列问题
二分搜索法:将最长上升子序列存储起来,使用二分法进行插入覆盖求取
将要查找的数取出来,与已有序列(max位)进行比对,寻找该数次大于的数,最长子序列就为i+1
小于·
左次小于
右次小于
*/
#include <stdio.h>
#include <string.h>
int main(){
    int n, row[3000], max;
    int lis[3000],lmax[3000],rmax[3000];  //lis存序列,l/rmax存各个数最长上升序列
    int low,high,mid;
    while(scanf("%d",&n) != EOF){
        //读数据
        for(int i = 0; i < n; i++) scanf("%d",&row[i]);
        
        //二分法查找各数左侧最长上升序列
        lis[0] = row[0];
        max = lmax[0] = 1;
        for(int i = 1; i < n; i++){
            low = 0;
            high = max;
            while(low <= high){
                mid = (low + high) / 2;
                if(row[i] > lis[max - 1]){        //大于最大值直接插入
                    lis[max] = row[i];    //插入,更新序列
                    lmax[i] = max + 1;        //更新序列值
                    max = max + 1;
                    break;
                }
                else if(row[i] <= lis[0]){   //小于最小值
                    lis[0] = row[i];
                    lmax[i] = 1;
                    break;
                }
                else if(row[i] <= lis[mid]){
                    if(row[i] > lis[mid-1] || max == 1){  //前夹值
                        lis[mid] = row[i];     //覆盖
                        lmax[i] = mid + 1;
                        break;
                    }else high = mid - 1;             //继续判断
                }
                else if(row[i] > lis[mid]){
                    if(row[i] < lis[mid+1] || max == 1){   //后夹值
                        lis[mid + 1] = row[i];  //覆盖
                        lmax[i] = mid + 2;
                        break;
                    }else low = mid + 1;
                }
                else if(row[i] == lis[mid]){   //同值
                    lmax[i] = mid + 1;    //注意mid是下标从0开始
                    break;
                }
            }
        }
        //二分法查找各数右侧最长上升序列
        //倒着来,那我们完全可以把原序列逆转一下
        for(int i = 0; i < n / 2; i++){
            int tmp = row[i];
            row[i] = row[n - i - 1];
            row[n - i - 1] = tmp;
        }
        memset(lis,0,3000);        //旧数组归零
        lis[0] = row[0];
        max = rmax[0] = 1;
        for(int i = 1; i < n; i++){
            low = 0;
            high = max;
            while(low <= high){
                mid = (low + high) / 2;
                if(row[i] > lis[max - 1]){        //大于最大值直接插入
                    lis[max] = row[i];    //插入,更新序列
                    rmax[i] = max + 1;        //更新序列值
                    max = max + 1;
                    break;
                }
                else if(row[i] <= lis[0]){   //小于最小值
                    lis[0] = row[i];
                    rmax[i] = 1;
                    break;
                }
                else if(row[i] <= lis[mid]){
                    if(row[i] > lis[mid-1] || max == 1){  //前夹值
                        lis[mid] = row[i];     //覆盖
                        rmax[i] = mid + 1;
                        break;
                    }else high = mid - 1;             //继续判断
                }
                else if(row[i] > lis[mid]){
                    if(row[i] < lis[mid+1] || max == 1){   //后夹值
                        lis[mid + 1] = row[i];  //覆盖
                        rmax[i] = mid + 2;
                        break;
                    }else low = mid + 1;
                }
                else if(row[i] == lis[mid]){   //同值
                    rmax[i] = mid + 1;    //注意mid是下标从0开始
                    break;
                }
            }
        }
        for(int i = 0; i < n / 2; i++){
            int tmp = rmax[i];
            rmax[i] = rmax[n - i - 1];
            rmax[n - i - 1] = tmp;
        }
        //计算最少剔除人数 = n - (lmax[i] + rmax[i] - 1)
        max = 0;           //其实没必要归0,因为左右相加肯定大
        for(int i = 0; i < n; i++){
            int tmp =lmax[i] + rmax[i] - 1;
            if(max < tmp) max = tmp;
        }
        printf("%d\n",n - max);
    }
    return 0;
}
全部评论
动态规划法这里用的是char,char最大也就127,如果一个子串的最长上升序列长度大于127,会溢出的
1 回复 分享
发布于 2022-04-02 17:35
动态规划法这里,lmax和rmax为什么是char型
点赞 回复 分享
发布于 2023-04-10 04:36 广东
在动态规划方法中,在计算从左到右依次增大的要求中,160对应的为什么是2呀
点赞 回复 分享
发布于 2022-02-06 23:53

相关推荐

来,说点可能被同行“骂”的大实话。🙊当初接数字马力Offer时,朋友都说:“蚂蚁的“内包”公司?你想清楚啊!”但入职快一年后的今天,我反而对他有了不一样的看法!🔹&nbsp;是偏见?还是信息差!之前没入职之前外面都在说什么岗位低人一等这类。实际上:这种情况不可至否,不能保证每个团队都是其乐融融。但我在的部门以及我了解的周边同事都还是十分好相处的~和蚂蚁师兄师姐之间也经常开一些小玩笑。总之:身份是蚂蚁公司给的,地位是自己挣的(一个傲娇女孩的自述)。🔹&nbsp;待遇?玩的就是真实!试用期工资全额发!六点下班跑得快(早9晚6或者早10晚7,动态打卡),公积金顶格交。别听那些画饼的,到手的钱和下班的时间才是真的(都是牛马何必难为牛马)。🔹&nbsp;能不能学到技术?来了就“后悔”!我们拥有权限直通蚂蚁知识库,技术栈多到学不完。说“学不到东西”的人,来了可能后悔——后悔来晚了(哈哈哈哈,可以不学但是不能没有)!💥&nbsp;内推地址:https://app.mokahr.com/su/ueoyhg❗我的内推码:NTA6Nvs走我的内推,可以直达业务部门,面试流程更快速,进度可查!今天新放HC,之前挂过也能再战!秋招已经正式开始啦~机会就摆在这,敢不敢来试一试呢?(和我一样,做个勇敢的女孩)
下午吃泡馍:数字马力的薪资一般哇,5年经验的java/测试就给人一万出头,而且刚入职第三天就让人出差,而且是出半年
帮你内推|数字马力 校招
点赞 评论 收藏
分享
给个offer灞:校友 是不是金die
点赞 评论 收藏
分享
评论
58
27
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务