百度松果菁英班--oj赛(第三次)

文章目录

  • 百度松果菁英班--oj赛(第三次)
    • 一、小码哥处理订单
    • 二、黑手党
    • 三、合数分解
    • 四、屠龙勇者
    • 五、数列分段
    • 六、小码哥爱数字
    • 七、泼墨淋漓
    • 八、栈的min
    • 九、括号家族

百度松果菁英班–oj赛(第三次)

一、小码哥处理订单

**题目:**假期快到了,小码哥在宾馆打暑假工。

小码哥需要处理接下来n天的住房信息,其中第i天宾馆有ri个房间可供租借。共有m份订单,每份订单用三个正整数描述,分别为dj,sj,tj,表示需要从第sj天到第tj天住房(包括第sj天和第tj天),每天需要出租dj个房间。

宾馆入住是先到先得,也就是说,小码哥按照订单给到的先后顺序来进行处理。如果在分配的过程中遇到一份订单使从第sj天到第tj天中有至少一天剩余的房间数量不足dj个,则需要停止工作,通知当前申请人修改订单。

由于到了假期,宾馆入住人数很多,小码哥需要知道,是否会有订单无法完全满足。如果有,小码哥需要通知修改的是哪个订单。他真的太累了,请你编程帮小码哥解决问题。

/**
	输入格式:第一行包含两个正整数n,m,表示天数和订单的数量;
第二行包含n个正整数,其中第i个数为ri,表示第i天可用于租借的房间数量,影响到所有需要在这天租借的订单;
接下来有m行,表示先后给到的订单。每行包含三个正整数dj,sj,tj,表示租借的数量,租借开始、结束分别在第几天。
每行相邻的两个数之间均用一个空格隔开。天数与订单均用从1开始的整数编号,且申请人编号范围为1到m,与订单号一样。
	输出格式:如果所有订单均可满足,则输出只有一行,包含一个整数0,否则(订单无法完全满足)输出两行,第一行输出一个负整数-1,第二行输出需要修改订单的申请人编号。
	样例1   输入:4 3
                2 5 4 3
                2 1 3
                3 2 4
                4 2 4
			输出:-1
				2
	备注
对于100%的数据,有1≤n, m ≤10^6,0≤ri≤10^9,0 

using namespace std;
const int N = 1e6 + 5;
int n, m, r[N], d[N], s[N], t[N];
bool check(int num){
    int sub[N], need[N];
    //给sub数组的值全部初始化为0
    memset(sub, 0, sizeof(sub));
    for(int i = 1; i <= num; i++){
        //数组的差分求值,防止累加到重复值
        sub[s[i]] += d[i];
        sub[t[i] + 1] -= d[i];
    }
    for(int i = 1; i <= n; i++){
        //前缀和求出每一天至少需要多少个房间
        need[i] = need[i - 1] + sub[i];
        //每一天需要的房间和出租房间比较
        if(need[i] > r[i]){//不满足出租要求
            return true;
        }
    }
    return false;
}
int main(){
    cin >> n >> m;
    for(int i = 1; i <= n; i++){
        cin >> r[i];
    }
    for(int i = 1; i <= m; i++){
        cin >> d[i] >> s[i] >> t[i];
    }
    int left = 1;
    int right = m;
    if(!check(m)){//当全部订单均满足
        cout << 0;
        return 0;
    }
    int mid, ans;
    while(left <= right){
        mid = left + (right - left) / 2;
        //当订单未满足时,查找第一天未满足的订单
        if(check(mid)){
            right = mid - 1;
            ans = mid;
        }else{//订单满足
            left = mid + 1;
        }
    }
    cout << "-1" << endl;
    cout << ans;
    return 0;
}

二、黑手党

**题目:**有n个人在玩游戏"黑手党”,这个游戏规则是每局必须有一个主持,(n -1)名选手。其中第i个人表示想玩ai局游戏且不当主持,请求出满足每人要求的最少的局数。

/**
	输入格式:第一行为人数n ;第二行为数列a 。
	输出格式:一行一个整数即为答案。
	样例1   输入:3
				3 2 2
			输出:4
	备注
		其中:3<=n≤ 1e5,1 ≤ai≤ 1e9
*/         
#include 
#define ll long long

using namespace std;
const int N = 1e5 + 5;
ll a[N], n, l, r, mid, ans, sum;
bool check(ll num){//满足每个人要求的最少局数返回true
    return num * (n - 1) >= sum;
}
int main(){
    cin >> n;
    for(int i = 0; i < n; i++){
        cin >> a[i];
        l = max(l, a[i]);//满足所有人要求的最少场数
        sum += a[i];//满足所有人要求的最多场数
    }
    r = sum;
    while(l <= r){
        mid = l + (r - l) / 2;
        //所有选手的平均场所大于等于最大场数时
        if(check(mid)){//此时局数多了
            r = mid - 1;
            ans = mid;
        }else{//所有选手的平均场所小于等于最大场数时,此时局数少了
            l = mid + 1;
        }
    }
    cout << ans;
    return 0;
}

三、合数分解

**题目:**给你一个正整数n,求最多能分成几个合数。若n为合数,它自身算一个合数。无解输出-1 。

/**
	输入格式:第一行一个正整数T;接下来T行每行表示一个测试数据。
	输出格式:每一行输出一个答案。
	样例1   输入:1
                10
			输出:2
	备注
		其中:T≤1e5,1≤n ≤1e9
*/         
#include 

using namespace std;
int t, n;
int main(){
    cin >> t;
    while(t--){
        cin >> n;
        if(n == 1 || n == 2 || n == 3 || n == 5 || n == 7 || n == 11){
            cout << "-1" << endl;//不能拆成合数要做特殊判断
        //可以被最小的合数4取余为0,4 -> 4  8 -> 4+4;取余为2,6 -> 4+2  10 -> 4 + 6
        }else if(n % 4 == 0 || n % 4 == 2){
            cout << n / 4 << endl;//拆分的合数数量
        //可以被最小的合数4取余为1,9 -> 4+4+1=4+5、13 -> 4+4+4+1=4+4+5
        }else if(n % 4 == 1 || n % 4 == 3){//取余为15 -> 4+4+4+3=4+4+7
            cout << n / 4 - 1 << endl;//拆分的合数数量
        }
    }
    return 0;
}

四、屠龙勇者

**题目:**很久很久以前,巨龙突然出现,带来灾难带走了公主又消失不见。后来,一名叫做小码哥的勇者战胜了巨龙,拯救了王国。现在,一头更可怕的多头喷火龙使王国再次面临毁灭。巨龙有n个头,每个头的直径为di,而国王有m个勇士,每个勇士最多只能砍一个头,且能力值为 w的勇士只能砍下直径不超过w的龙头。现在请你计算这些勇士能否消灭这只巨龙。

/**
	输入格式:输入共三行,第一行两个正整数 n, m 满足0 

using namespace std;
const int N = 1e5 + 5;
int n, m, d[N], w[N]; 
int main(){
    cin >> n >> m;
    for(int i = 1; i <= n; i++){
        cin >> d[i];
    }
    for(int i = 1; i <= m; i++){
        cin >> w[i];
    }
    //核心是间两个数组排序比较即可
    sort(d + 1, d + n + 1);
    sort(w + 1, w + m + 1);
    if(n > m){
        cout << "NO";
        return 0;
    }
    for(int i = 1; i <= n; i++){
        if(w[i + m - n] < d[i]){//从后等数量比较
            cout << "NO";
            return 0;
        }
    }
    cout << "YES";
    return 0;
}

五、数列分段

**题目:**对于给定的一个长度N的正整数数列Ai,现要将其分成连续的若干段,并且每段和不超过M(可以等于M ),问最少能将其分成多少段使得满足要求。

/**
	输入格式:第一行包含两个正整数N,M,表示了数列Ai的长度与每段和的最大值;第二行包含N个空格隔开的正整数Ai,如题目所述。
	输出格式:一个正整数,输出最少划分的段数。
	样例1   输入:5 6
				4 2 4 5 1
			输出:3
	备注
		其中: 1≤N ≤10^5,N 

using namespace std;
const int N = 1e5 + 5;
int A[N], n, m, ans, s;
int main(){
    cin >> n >> m;
    int sum = 0;
    for(int i = 1; i <= n; i++){
        cin >> A[i];
        s = A[1];
        //当连续的值相加小于每段的最大值,则组成一个段,形成最少段
        if(sum + A[i] <= m){
            sum += A[i];
        }else{//当连续的值相加大于每段的最大值,重新赋予一段
            sum = A[i];
            ans++;
        }
    }
    //由于题目没有说是否会有直接大于每段的最大值,做个简单的判断
    cout << (s > m ? ans : ans + 1);
    return 0;
}

六、小码哥爱数字

**题目:**小码哥很喜欢数字,有一天他找到老师给他出一道有关数字的题目。老师给他一个位数很多的正整数N(不超过250 位),让小码哥去掉其中任意k个数字后剩下的数字按原左右次序将组成一个新的非负整数。小码哥觉得老师在刁难他(因为小码哥才一年级),所以他找身为程序员的你编程对给定的N和 k ,寻找一种方案使得剩下的数字组成的新数最小。

/**
	输入格式:N(正整数,不超过250位),不必考虑前导0;k(需要删除的数字个数),保证有输出,即k小于n的位数。
	输出格式:最后剩下的最小数。
	样例1   输入:175438
				4
			输出:13
*/         
#include 

using namespace std;
string n;
int k, ld0;
int main(){
    cin >> n;
    cin >> k;
    int len = n.length();
    while(k--){
        //本题解法主要是求剩下的数最小,只需从左往右,将当前数大于后面数删除即可
        for(int i = 0; i < len; i++){
            if(n[i] > n[i + 1]){//前一个数大于后一个数,则删除当前数
                for(int j = i; j <= len; j++){
                    n[j] = n[j + 1];//将后面的数全部往前挪
                }
                len--;
                break;
            }
        }
    }
    //处理前导0
    while(ld0 < len - 1 && n[ld0] == '0'){
        ld0++;
    }
    for(int i = ld0; i < len; i++){
        cout << n[i];
    }
    return 0;
}

七、泼墨淋漓

**题目:**小码哥有n幅画,每幅画都有一个编号 ai,编号为非负数且可以相同。他想改变一些画的编号,使得n幅画使用的不同编号数量不超过k ( 1<=k<=n <=200000 ) ,问最少需要改变多少幅画的编号?

/**
	输入格式:第一行输入n,k ;第二行输入ai 。
	输出格式:输出需要改变编号的画的最少数量。
	样例1   输入:5 2
				1 1 2 2 5
			输出:1
*/         
#include 

using namespace std;
const int N = 2e5 + 5;
int n, k, a[N], b[N], ans;
bool cmp(int a, int b){
    return a > b;//降序
}
int main(){
    cin >> n >> k;
    for(int i = 0; i < n; i++){
        cin >> a[i];
        //将相同序号的数量累加
        b[a[i]] += 1;
    }
    sort(b, b + N, cmp);
    //排完序后从第k个开始,将后面的数字相加
    //即是需要改变的编号数量
    for(int i = k; i < n; i++){
        ans += b[i];
    }
    cout << ans;
    return 0;
}

八、栈的min

**题目:**小码哥又被安排任务了,这次他需要要设计一个堆栈,他除了可以满足正常的栈的操作以外,还要可以输出栈内最小元素值。

o(n)的算法小码哥当然会,小码哥为了给上司一个惊喜决定设计一个o(1)就能输出的程序,自然这个任务就被丢给你了。

  • c(x)-将元素x插入栈中
  • y()-移除栈顶元素
  • s()-得到栈顶元素
  • g_m()-得到栈中最小元素
/**
	输入格式:第一行输入操作个数n(整数);第2行到第n+1行输入一个整数t分别依次代表上述4种操作;当t == 1时,会额外输入一个整数x。
	输出格式:当t == 3或t ==4时,输出相应数据,每一行输出一个数据。
	样例1   输入:6
                1 3
                1 4
                3
                4
                2
                3
			输出:4
                3
                3
	备注
		其中: 1<=t<=4;操作命令总数[0,100]。
*/         
#include 

using namespace std;
stack stackValue;//定义常规栈
stack stackMin;//定义栈求最小元素

void push(int x){//将元素x入栈
    stackValue.push(x);
    if(stackMin.empty() || stackMin.top() >= x){
        stackMin.push(x);
    }
}

void pop() {//移除栈顶
    if(stackMin.top() == stackValue.top()){
        stackMin.pop();
    }
    stackValue.pop();
}

int top(){//得到栈顶元素
    return stackValue.top();
}

int getMin(){//得到栈顶元素
    return stackMin.top();
}

int main(){
    int n;
    cin >> n;
    while(n--){
        int a;
        cin >> a;
        switch(a){
            case 1: //入栈
                int x;
                cin >> x;
                push(x);
                break;
            case 2: //移除栈顶
                pop();
                break;
            case 3: //出栈,得到栈顶元素
                cout << top() << endl;
                break;
            case 4: //得到栈中的最小元素
                cout << getMin() << endl;
                break;
        }
    }
    return 0;
}

九、括号家族

**题目:**小码哥在研究只有左括号和右括号组成的序列。给出一个括号序列,求出最长合法子串和它的数量(合法的定义:这个序列中左右括号匹配)。

例如: (()不合法,)()(也不合法,但()()(())合法。

/**
	输入格式:输入一行只有左括号和右括号组成的序列(只包含小括号)。
	输出格式:输出两个数字:最长子字符串的长度,以及此类子字符串的数量,如果没有这样的子字符串,则输出 0 1 。
	样例1   输入:()()))()()(()
			输出:4 2
	备注
    其中:字符串长度小于等于10^6
*/         
#include 

using namespace std;
const int N = 1e6 + 5;
struct NODE{
    char c;//括号
    int index;//索引
};
bool tag[N];//标记位置
string str;
stack s;//初始栈

int main(){
    cin >> str;
    int len = str.length();
    for(int i = 0; i < len; i++){
        //如果栈顶元素为( 下一个想要入栈的为),则将(出栈,并给两者标记
        if(!s.empty() && s.top().c == '(' && str[i] == ')'){
            tag[i] = true;//标记为true,用来后续查看最长的子串
            tag[s.top().index] = true;
            s.pop();
        }else{//不符合则间括号入栈处理
            s.push({
                str[i],
                i
            });
        }
    }

    int maxlen = 0, tmp = 0;
    //等于len是将进行最后的最大子串比较
    for(int i = 0; i <= len; i++){//求最大的子串长度
        if(tag[i]){
            tmp++;//计算子串的长度
        }else{
            maxlen = max(maxlen, tmp);//取最大的子串长度
            tmp = 0;//每次循环比较结束重置值
        }
    }

    int count = 0;
    for(int i = 0; i <= len; i++){//求最大子串的数量
        if(tag[i]){
            tmp++;//计算子串的长度
        }else{
            if(tmp == maxlen){
                count++;//当子串为最大子串时,数量加1
            }
            tmp = 0;//每次循环比较结束重置值
        }
    }

    if(maxlen){
        cout << maxlen << " " << count;
    }else{
        cout << "0 1";
    }
    return 0;
}

记录每一个学习瞬间

你可能感兴趣的:(算法,c++)