[算法进阶0x10]基本数据结构C作业总结

t1-Supermarket 超市利润

题目大意

给定n个商品,每个商品有利润pi和过期时间di。每天只能卖一个商品,过期商品不能卖。求如何安排每天卖的商品可以使收益最大。

分析

一开始打了一个复杂度跑不满\(n^2\)的暴力发现T掉了,就换成了\(nlogn\)的算法,但是依旧是T掉了,而且T飞掉了,看到洛谷里有人发帖子说只能用cin才A掉了。
首先,很明显最优的是一直在卖东西,那么可以将所有的已经当前决定是卖掉的物品放入一个小根堆中。
我们先将这些物品按照日期从小到大排序
分成3种情况讨论:

  • 如果当前的过期日期=堆中元素个数,说明在过期最后一天不能卖这个物品了,那么就把堆顶取出,弹入较大的。
  • 如果当前的过期日期<堆中元素个数,说明可以直接卖掉,那么直接弹入堆中。
  • 如果当前的过期日期>堆中元素个数,不存在这种情况,直接跳过

每一次操作后都要维护堆。
ps.前辈的经验,不要用while (scanf("%d", &n)!=EOF),T到自己怀疑人生,而且自己学校的oj还有手写堆,更加怀疑人生。

ac代码

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ll long long 
#define N 10005
#pragma GCC optimize(2)
using namespace std;
template <typename T>
inline void read(T &x) {
    x = 0; T fl = 1;
    char ch = 0;
    while (ch < '0' || ch > '9') {
        if (ch == '-') fl = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    x *= fl;
}
struct node {
    int p, d;
    inline bool operator <(const node &rhs) const {
        return d < rhs.d;
    }
}a[N];
int n, ans;
bool vis[N];
struct Heap {
    int heap[N], tot;
    inline void up(int u) {
        while (u > 1) {
            if (heap[u] < heap[u >> 1]) {
                swap(heap[u], heap[u >> 1]);
                u >>= 1;
            }
            else break;
        }
    }
    inline void insert(int v) {
        heap[++ tot] = v;
        up(tot);
    }
    inline void down(int u) {
        int s = u << 1;
        while (s <= tot) {
            if (s < tot && heap[s] > heap[s + 1]) s ++;
            if (heap[s] < heap[u]) {
                swap(heap[s], heap[u]);
                u = s;
                s = u << 1;
            }
            else break;
        }
    }
    inline void pop() {
        heap[1] = heap[tot --];
        down(1);
    }
    inline int get_top() {
        return heap[1];
    }
    inline void clear() {
        memset(heap, 0, sizeof(heap));
        tot = 0;
    }
}heap;
int main() {
    while (cin>> n) {
        heap.clear();
        ans = 0;
        for (register int i = 1; i <= n; i ++) {
            read(a[i].p); read(a[i].d);
        }
        sort(a + 1, a + 1 + n);
        for (register int i = 1; i <= n; i ++) {
            if (a[i].d == heap.tot) {
                if (a[i].p > heap.get_top()) {
                    heap.pop();
                    heap.insert(a[i].p);
                }
            }
            else if (a[i].d > heap.tot) {
                heap.insert(a[i].p);
            }
        }
        for (register int i = 1; i <= heap.tot; i ++) 
            ans += heap.heap[i];
        printf("%d\n", ans);
    }
    return 0;
}

t2-Sequence

题目大意

给你m个长度为n的数列,让你从m个队列中每一个数列中取出一个数,求出字典序的第n小。

分析

这是k小堆的模板,其实就是维护一个长度固定的堆,不做赘述。

ac代码

#include <bits/stdc++.h>
#define pii pair<int, int>
#define ll long long
#define ms(a, b) memset(a, b, sizeof(a))
#define inf 0x3f3f3f3f
using namespace std;
template <typename T>
inline void read(T &x) {
    x = 0; T fl = 1;
    char ch = 0;
    while (ch < '0' || ch > '9') {
        if (ch == '-') fl = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    x *= fl;
}
int n, m;
int a[2005], b[2005];
void merge(int *a, int *b, int *c, int n) {
    priority_queue<pii, vector<pii>, greater<pii> >q;
    for (int i = 0; i < n; i ++) q.push(pii(a[i] + b[0], 0));
    for (int i = 0; i < n; i ++) {
        int x = q.top().first, y = q.top().second; 
        q.pop();
        c[i] = x;
        if (y + 1 < n) q.push(pii(x - b[y] + b[y + 1], y + 1));
    }
}
int main() {
    int cas; read(cas);
    while (cas --) {
        read(m); read(n);
        for (int i = 0; i < n; i ++) read(b[i]);
        sort(b, b + n);
        for (int i = 1; i < m; i ++) {
            for (int j = 0; j < n; j ++) read(a[j]);
            sort(a, a + n);
            merge(b, a, b, n);
        }
        for (int i = 0; i < n; i ++) printf("%d ", b[i]);
        puts("");
    }
    return 0;
}

t3-数据备份 Backup

不做赘述
题解链接:https://www.cnblogs.com/chhokmah/p/10557925.html

t4-黑匣子(blackbox)

分析

开两个堆,一个大根堆维护1~i-1小元素,一个小根堆维护i~n小元素
添加元素时,如果元素小于大根堆堆顶,那么把大根堆堆顶出堆,将此元素加入大根堆
否则将元素加入小根堆
查询值即为小根堆顶,查询完后将小根堆顶加入大根堆

ac代码

#include <bits/stdc++.h>
#define ll long long
#define ms(a, b) memset(a, b, sizeof(a))
#define inf 0x3f3f3f3f
#define N 200005
using namespace std;
template <typename T>
inline void read(T &x) {
    x = 0; T fl = 1;
    char ch = 0;
    while (ch < '0' || ch > '9') {
        if (ch == '-') fl = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    x *= fl;
}
priority_queue<int> qmax;
priority_queue<int, vector<int>, greater<int> >qmin;
int a[N];
int n, m;
int main() {
    read(m); read(n);
    for (int i = 1; i <= m; i ++) read(a[i]);
    int j = 1;
    for (int i = 1; i <= n; i ++) {
        int x; read(x);
        for (; j <= x; j ++) {
            if (!qmax.empty() && a[j] <= qmax.top()) {
                qmin.push(qmax.top());
                qmax.pop();
                qmax.push(a[j]);
            }
            else qmin.push(a[j]);
        }
        printf("%d\n", qmin.top());
        qmax.push(qmin.top());
        qmin.pop(); 
    }
    return 0;
}

t5-生日礼物

题目大意

n个数分成m段,使其每一段和和最大值

分析

不做赘述:https://www.cnblogs.com/chhokmah/p/10558237.html

t6-合并果子

太简单了,优先队列取最小的两个,再把和压回去,写堆也可以。

ac代码

#include <bits/stdc++.h>
#define ll long long
#define ms(a, b) memset(a, b, sizeof(a))
#define inf 0x3f3f3f3f
using namespace std;
template <typename T>
inline void read(T &x) {
    x = 0; T fl = 1;
    char ch = 0;
    while (ch < '0' || ch > '9') {
        if (ch == '-') fl = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    x *= fl;
}
priority_queue<int, vector<int>, greater<int> >q;
int n;
ll ans = 0;
int main() {
    read(n);
    for (int i = 1; i <= n; i ++) {
        int x;
        read(x);
        q.push(x);
    }
    for (int i = 1; i < n; i ++) {
        int x1 = q.top(); 
        q.pop();
        int x2 = q.top();
        q.pop();
        ans += x1 + x2;
        q.push(x1 + x2);
    }
    printf("%lld", ans);
    return 0;
}

t7-荷马史诗

分析

首先明确需要维护什么。转换一下题意,不难知道我们需要维护的是最短的带权路径之和和该哈夫曼树的高度。然后便是如何维护,由于不需要知道哈夫曼树的具体形态,我们便可以按照哈夫曼树的构造方式,将当前最小的K个节点合并为1个父节点,直至只有一个父节点。看到“将最小K个节点合并”便可以明确使用优先队列(二叉堆)进行维护。
最后,我们需要注意一个细节。因为每次都是将k个节点合并为1个(减少k-1个),一共要将n个节点合并为1个,如果(n-1)%(k-1)!=0 则最后一次合并时不足k个。也就表明了最靠近根节点的位置反而没有被排满,因此我们需要加入k-1-(n-1)%(k-1)个空节点使每次合并都够k个节点(也就是利用空节点将其余的节点挤到更优的位置上)。

ac代码

#include <bits/stdc++.h>
#include <ext/pb_ds/priority_queue.hpp> 
#define ll long long
#define ms(a, b) memset(a, b, sizeof(a))
#define inf 0x3f3f3f3f
using namespace std;
template <typename T>
inline void read(T &x) {
    x = 0; T fl = 1;
    char ch = 0;
    while (ch < '0' || ch > '9') {
        if (ch == '-') fl = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    x *= fl;
}
struct node {
    ll w, h;
    node() {
        w = 0, h = 0;
    }
    node(ll w, ll h): w(w), h(h){}
    bool operator <(const node &rhs) const {
        if (w != rhs.w) return w > rhs.w;
        return h > rhs.h;
    }
};
__gnu_pbds::priority_queue <node, std::less<node>, __gnu_pbds::pairing_heap_tag> q;
int n, k, cnt;
ll tmp, mx, ans;
int main() {
    read(n); read(k);
    for (int i = 1; i <= n; i ++) {
        read(tmp);
        q.push(node(tmp, 1));
    }
    if ((n - 1) % (k - 1) != 0) cnt = k - 1 -(n - 1) % (k - 1);
    for (int i = 1; i <= cnt; i ++) {
        q.push(node(0, 1));
    }
    cnt += n;
    while (cnt > 1) {
        tmp = mx = 0;
        for (int i = 1; i <= k; i ++) {
            tmp += q.top().w;
            mx = max(mx, q.top().h);
            q.pop();
        }
        ans += tmp;
        q.push(node(tmp, mx + 1));
        cnt -= k - 1;
    }
    printf("%lld\n%lld\n", ans, q.top().h - 1);
    return 0;
}

t8-双栈排序

分析

首先必然要先考虑是否有解。对于没有解的情况,必然是当到了某一个数x0时,栈1,栈2队首元素都不能弹出,并且x0要比栈1、2的队首元素都要大,这时就不能排序了。
所以考虑什么时候A、B不能在同一个栈中的情况:
当且仅当,A<B,并且存在C,使得A>C.并满足A位置在B前面,B位置在C前面。就是说,由于C的存在,A不能pop掉,但是B放进去,A就永远pop不了了。
这样就可以找到所有不能和x0在同一个栈里的所有位置上的数了。
判断无解时,将所有上述的A和B之间连一条无向边,用二分图染色或者带偏移量的并查集都可以。
输出时,因为要字典序最小,所以第一个元素必然要放进栈1,这样可以预处理出来所有数要进入哪一个栈。能进栈1的都进栈1.
然后模拟实现,每次先要判断是否可以pop掉栈顶元素,然后按照之前的预处理的方案放进数就可以了。

ac代码

#include <bits/stdc++.h>
#define ll long long
#define ms(a, b) memset(a, b, sizeof(a))
#define inf 0x3f3f3f3f
#define N 10005
using namespace std;
template <typename T>
inline void read(T &x) {
    x = 0; T fl = 1;
    char ch = 0;
    while (ch < '0' || ch > '9') {
        if (ch == '-') fl = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    x *= fl;
}
struct edge {
    int to, nt;
}E[N];
int H[N], fmn[N], a[N], p[3][N], c[N], l[N];
int n, cnt;
void add_edge(int u, int v) {
    E[++ cnt] = (edge){v, H[u]}; 
    H[u] = cnt;
}
void dfs(int u, int col) {
    if (!c[u]) c[u] = col;
    else if (c[u] != col) {
        printf("0\n");
        exit(0);
    }
    else return;
    for (int e = H[u]; e; e = E[e].nt) {
        int v = E[e].to;
        dfs(v, 3 - col);
    }
}
int main() {
    read(n); 
    for (int i = 1; i <= n; i ++) {
        read(a[i]);
    }
    fmn[n] = a[n];
    for (int i = n - 1; i >= 1; i --) fmn[i] = min(fmn[i + 1], a[i]);
    p[1][0] = 1002, c[n + 1] = 1; p[2][0] = 1002; a[n + 1] = 1001;
    for (int i = 1; i < n - 1; i ++) {
        for (int j = i + 1; j < n; j ++) {
            if (a[i] < a[j] && fmn[j + 1] < a[i]) {
                add_edge(i, j);
                add_edge(j, i);
            }
        }
    }
    for (int i = 1; i <= n; i ++) {
        if (!c[i]) dfs(i, 1);
    }
    int x = 1;
    for (int i = 1; i <= n + 1; i ++) {
        while (233) {
            if (p[1][l[1]] == x) {
                x ++;
                l[1] --;
                printf("b ");
            }
            else if (p[2][l[2]] == x) {
                x ++;
                l[2] --;
                printf("d ");
            }
            else break;
        }
        p[c[i]][++ l[c[i]]] = a[i];
        if (i < n + 1) printf("%c ", char(2 * c[i] + 95));
    }
    return 0;
}

t9-Subway tree systems

分析

树的最小表示法:从下向上把子树排序,这样向上,最后表示出来的就是最小表示的了,那么就有序了,就可以直接比较字符串就能判断两棵树是否相同。

ac代码

#include <vector>
#include <cstdio>
#include <string>
#include <algorithm>
#define ms(a, b) memset(a, b, sizeof(a))
#define LL long long
using namespace std;
char s[3005];
string dfs(int x) {
    vector<string> ch;
    while (s[x] == '0') {
        string t = "(" + dfs(x + 1);
        ch.push_back(t);
        x += t.length();
    }
    sort(ch.begin(), ch.end());
    string res = "";
    vector<string>::iterator it;
    for (it = ch.begin(); it != ch.end(); it ++) res += *it;
    return res + ")";
}
int main() {
    int cas; 
    scanf("%d", &cas);
    for (int _t = 1; _t <= cas; _t ++) {
        scanf("%s", s);
        string t = dfs(0);
        scanf("%s", s);
        if (dfs(0) == t) puts("same");
        else puts("different");
    }
    return 0;
}

t10-Sliding Window 滑窗

分析

单调队列模板

ac代码

# include <cstdio>
# include <cstring>
# include <algorithm>
# include <ctype.h>
# include <iostream>
# include <cmath>
# include <map>
# include <vector>
# include <queue>
# define LL long long
# define ms(a,b) memset(a,b,sizeof(a))
# define ri (register int)
# define inf (0x7f7f7f7f)
# define pb push_back
# define fi first
# define se second
# define pii pair<int,int>
# define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
# pragma GCC optimize(2)
using namespace std;
inline int gi(){
    int w=0,x=0;char ch=0;
    while(!isdigit(ch)) w|=ch=='-',ch=getchar();
    while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    return w?-x:x;
}
# define N 1000005
deque<int>qmax,qmin;
int ansmin[N],ansmax[N],a[N];
int n,k;
void write(int x){
    if (x>9) write(x/10);
    putchar(x%10+'0');
}
int main(){
    n=gi(),k=gi();
    for (int i=1;i<=n;i++) a[i]=gi();
    for (int i=1;i<=n;i++){
        while (!qmax.empty()&&i-qmax.front()+1>k) qmax.pop_front();
        while (!qmin.empty()&&i-qmin.front()+1>k) qmin.pop_front();
        while (!qmax.empty()&&a[i]>a[qmax.back()]) qmax.pop_back();
        while (!qmin.empty()&&a[i]<a[qmin.back()]) qmin.pop_back();
        qmax.push_back(i); qmin.push_back(i); 
        ansmax[i]=a[qmax.front()]; ansmin[i]=a[qmin.front()];
    }
    for (int i=k;i<=n;i++) printf("%d ",ansmin[i]); puts("");
    for (int i=k;i<=n;i++) printf("%d ",ansmax[i]);
    return 0;
}

转载于:https://www.cnblogs.com/chhokmah/p/10558496.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值