[Offer收割]编程练习赛28

本文提供了三个算法题目的详细解答过程,包括使用深度优先搜索解决时间问题、利用马拉车算法求解回文子串数量以及通过优先队列处理会议室预订冲突等。文章深入浅出地介绍了这些经典算法的应用场景及实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

The Next Moment

题解

直接用DFS处理,时,分,秒,然后一个个的枚举,就可以了,将不符合条件的去掉,取个最小的,对于第二天的时间需要加个243600,这道题目做的我都尴尬了,直接%d:%d:%d竟然过不了,只有先读取字符串,然后用字符串处理才可以,也是让我刷新了对出题人的奇葩程度

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>

#define FIN freopen("input.txt", "r", stdin)
#define FOUT freopen("output.txt", "w", stdout)
using namespace std;
const int MAXN = 100 + 5;
const int INF = 0x3f3f3f3f;
bool vis[MAXN];
vector<int> vs;
int time_ts[3], mtime_ts[3];
int mc;
int limits[3] = {24, 60, 60};
char ch[MAXN];
void dfs(int x, int &nt) {
    if(x >= 3) {
        int ys = time_ts[0] * 3600 + time_ts[1] * 60 + time_ts[2];
        int c = ys - nt;
        if(c <= 0) {
            c += limits[0] * 3600;
        }
        if(c < mc) {
            mc = c;
            mtime_ts[0] = time_ts[0];
            mtime_ts[1] = time_ts[1];
            mtime_ts[2] = time_ts[2];
        }
        return;
    }
    for(int i = 0; i < vs.size(); i ++) {
        for(int j = 0; j < vs.size(); j ++) {
            int t = vs[i] * 10 + vs[j];
            if(t >= limits[x]) continue;
            time_ts[x] = t;
            dfs(x + 1, nt);
        }
    }
}

void scan(int &h, int &m, int &s){
    scanf("%s", ch);
    int len = strlen(ch);
    int k = 0;
    for(int i = 0;i < len;i ++){
        if(isdigit(ch[i])) {
            ch[k] = ch[i];
            k ++;
        }
    }
    h = (ch[0] - '0') * 10 + ch[1] - '0';
    m = (ch[2] - '0') * 10 + ch[3] - '0';
    s = (ch[4] - '0') * 10 + ch[5] - '0';
}

int main() {
    int _;
    //FIN;
    //FOUT;
    scanf("%d", &_);
    while(_ --) {
        int hh, mm, ss;
        scan(hh, mm, ss);
        //scanf("%d:%d:%d", &hh, &mm, &ss);
        memset(vis, false, sizeof(vis));
        vs.clear();
        int nt = hh * 3600 + mm * 60 + ss;
        int thh = hh, tmm = mm, tss = ss;
        for(int i = 0; i < 2; i ++) {
            vis[hh % 10] = true;
            hh /= 10;
            vis[mm % 10] = true;
            mm /= 10;
            vis[ss % 10] = true;
            ss /= 10;
        }
        for(int i = 0; i < MAXN; i ++) {
            if(vis[i]) vs.push_back(i);
        }
        mtime_ts[0] = thh;
        mtime_ts[1] = tmm;
        mtime_ts[2] = tss;
        mc = limits[0] * 3600;
        dfs(0, nt);
        printf("%02d:%02d:%02d\n", mtime_ts[0], mtime_ts[1], mtime_ts[2]);
    }
    return 0;
}

回文子串的数量

题解

题目意思基本可以理解为,求解这个字符串存在多少个回文子串,我们先提一个概念就是,如果你知道以i为中心的最长回文子串,那么该最长回文子串覆盖的区间中存在的回文子串的个数就是len2,所以就将问题转换为了求解以每一个位置为中心的最长回文子串,接下来就可以用很多方法了,比如后缀数组可以解决,不过用经典的马拉车来处理回文串问题是非常好的解决方案,至于马拉车算法是如何实现的,不会的,大家可以百度看看。

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>

#define FIN freopen("input.txt", "r", stdin)
#define FOUT freopen("output.txt", "w", stdout)
using namespace std;
typedef long long LL;
const int MAXN = 8e5 + 5;
const int INF = 0x3f3f3f3f;

int len[MAXN << 1];
char S[MAXN];
int sz;
char tS[MAXN << 1];

void solve() {
    int mx = 0, id = 0;
    for(int i = 1; i < sz; i ++) {
        if(mx > i) {
            len[i] = min(len[2 * id - i], mx - i);
        } else {
            len[i] = 1;
        }
        while(i - len[i] >= 1 && i + len[i] < sz && tS[i + len[i]] == tS[i - len[i]]) len[i] ++;
        if(len[i] + i > mx) {
            mx = len[i] + i;
            id = i;
        }
    }
    LL ans = 0;
    for(int i = 1; i < sz; i ++) {
        ans += len[i] / 2;
        //printf("[%d]\n", len[i]);
    }
    printf("%lld\n", ans);
}

int main() {
    while(~scanf("%s", S)) {
        sz = 1;
        int len_t = strlen(S);
        memset(len, 0, sizeof(len));
        for(int i = 0; i < len_t; i ++) {
            tS[sz ++] = '#';
            tS[sz ++] = S[i];
        }
        tS[sz ++] = '#';
        solve();
    }
    return 0;
}

紧张的会议室

题解

题目很简单,解决方案个人也很简单,很明显,数据很大Si,Ei都有109,所以常规方案不能做,先想到离散,或者说只处理出现的边界,所以先将所有的数据离线,然后可以得到最多2×105个数据,然后从左往右维护两个优先队列,第一个优先队列,保存的最开始的会议时间,第二优先队列维护的后面尝试加入的会议时间,如果第一个优先队列的个数M,那么第二个优先队列中的所有会议都是冲突的,优先队列排序是针对结束时间了,用了类似于单调队列的处理思路。

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <unordered_set>
#include <vector>

#define FIN freopen("input.txt", "r", stdin)
#define FOUT freopen("output.txt", "w", stdout)
using namespace std;
typedef long long LL;
const int MAXN = 4e5 + 5;
const int INF = 0x3f3f3f3f;

int N, M;
unordered_set<int> usi;

struct t_seq{
    int s, t, id;
    t_seq(int s, int t):s(s), t(t){

    }
    bool operator > (const t_seq & p) const{
        return !(*this < p);
    }
    bool operator < (const t_seq &p) const{
        return t < p.t;
    }
};

bool cmp(const t_seq & p, const t_seq & q){
    return p.s < q.s;
}


vector<t_seq> v1, v2;
vector<int> t_list;
priority_queue<t_seq, vector<t_seq>, greater<t_seq> > pqt1, pqt2;
bool vis[MAXN];

int main() {
    while(~scanf("%d%d", &N, &M)){
        v1.clear();
        v2.clear();
        t_list.clear();
        usi.clear();
        while(!pqt1.empty()) pqt1.pop();
        while(!pqt2.empty()) pqt2.pop();
        memset(vis, false, sizeof(vis));
        int t, s;
        for(int i = 0;i < N;i ++){
            scanf("%d%d", &s, &t);
            v1.push_back(t_seq(s, t));
            usi.insert(s);
            usi.insert(t);
        }

        int Q;
        scanf("%d", &Q);
        for(int i = 0;i < Q;i++){
            scanf("%d%d", &s, &t);
            t_seq ts(s, t);
            ts.id = i;
            v2.push_back(ts);
            usi.insert(s);
            usi.insert(t);
        }
        for(unordered_set<int>::iterator it = usi.begin(); it != usi.end();it ++){
            t_list.push_back(*it);
        }

        sort(t_list.begin(), t_list.end());
        sort(v1.begin(), v1.end(), cmp);
        sort(v2.begin(), v2.end(), cmp);

        int p1 = 0, p2 = 0;
        for(int i = 0;i < t_list.size();i ++){
            while(pqt1.size() && pqt1.top().t <= t_list[i]) pqt1.pop();
            while(pqt2.size() && pqt2.top().t <= t_list[i]) pqt2.pop();
            while(p1 < v1.size() && v1[p1].s == t_list[i]){
                pqt1.push(v1[p1 ++]);
            }
            while(p2 < v2.size() && v2[p2].s == t_list[i]){
                pqt2.push(v2[p2 ++]);
            }
            if(pqt1.size() >= M) {
                while(!pqt2.empty()){
                    vis[pqt2.top().id] = true;
                    pqt2.pop();
                }
            }
        }
        for(int i = 0;i < Q;i ++){
            printf("%s\n", vis[i] ? "NO" : "YES");
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值