《算法竞赛进阶指南》 0x11 ~ 0x12 代码 + 杂谈

本文深入探讨了数据结构中的核心概念——栈、队列和双端队列,提供了详细的代码实现和应用场景解析,包括单调栈、单调队列、双端队列的应用实例,以及如何利用这些数据结构解决复杂问题。

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

0x11 栈

  1. 单调栈 栈的基本操作
class MinStack {
public:
    /** initialize your data structure here. */
    int a[5050];
    int mi[5050];
    int tops;
    
    MinStack() {
        tops=0;
    }
    
    void push(int x) {
        a[++tops]=x;
        if(tops==1){
            mi[tops]=x;
        }else{
            mi[tops]=min(mi[tops-1],x);
        }
    }
    
    void pop() {
        tops--;
    }
    
    int top() {
        return a[tops];
    }
    
    int getMin() {
        return mi[tops];
    }
};
  1. 编辑器
    对顶栈 栈的应用 f数组存最大sum
#include <bits/stdc++.h>
using namespace std;

const int maxn = 1e6 + 5 ;
const double pi = acos(-1.0);

int n, m;
int A[maxn], Atop;
int B[maxn], Btop;
int sum[maxn], f[maxn];

signed main() {
    cin >> n;
    string cmd;
    f[0]=-0x3f3f3f3f;
    while(n--) {
        cin >> cmd;
        if(cmd[0] == 'I') {
            cin >> m;
            A[++Atop] = m;
            sum[Atop] = sum[Atop - 1] + A[Atop];
            f[Atop] = max(sum[Atop], f[Atop - 1]);
        } else if(cmd[0] == 'Q') {
            cin >> m;
            cout << f[m] << endl;
        } else if(cmd[0] == 'D') {
            if(Atop)
                Atop--;
        } else if(cmd[0] == 'R') {
            if(Btop == 0)
                continue;
            A[++Atop] = B[Btop--];
            sum[Atop] = sum[Atop - 1] + A[Atop];
            f[Atop] = max(f[Atop - 1], sum[Atop]);
        } else if(cmd[0] == 'L') {
            if(Atop == 0)
                continue;
            B[++Btop] = A[Atop--];
        }
    }
    return 0;
}
  1. 火车进出栈问题
    当入栈 + 出栈火车达到n时 输出 出战的在把栈内pop出即可
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5 ;
const double pi = acos(-1.0);

int n, m;
int sta[maxn], top;
int que[maxn], head, tail;
int ans;

void dfs(int num) {
    if(ans >= 20)
        return ;
    if(num == n + 1) {
        for(int i = 1; i <= tail; i++) {
            cout << que[i];
        }
        for(int i = top; i; i--) {
            cout << sta[i];
        }
        cout << endl;
        ans++;
        return ;
    }
    if(top) {
        que[++tail] = sta[top--];
        dfs(num);
        sta[++top] = que[tail--];
    }
    sta[++top] = num;
    dfs(num + 1);
    --top;
}

signed main() {
    cin >> n;
    dfs(1);
    return 0;
}
  1. 进出站序列问题 卡特兰数 1 0 序列 一般 1 <= 0 但是最终数量上 0 == 1
    DP 直接膜大数乘除 超时。。。。。。。。。。。orz
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn =2e5 + 5 ;
const int jz = 1e9;

int res[maxn], tt;
bool pr[maxn];
int p[maxn],cnt,chu[maxn];

void init(){
    pr[1]=pr[0]=1;
    for(int i = 2; i < maxn; i++ ){
        if(!pr[i]){
            p[cnt ++ ] = i;
            for(int j = (i << 1); j < maxn; j += i){
                pr[j] = 1;
            }
        }
    }
}

int ges(int n, int ps){
    int s = 0;
    while(n){
        s += n/ps;
        n/=ps;
    }
    return s;
}

void mul(int b){
    int r = 0;
    for(int i = 0;  i <= tt; i ++ ) {
        res[i] = res[i] * b + r;
        r = res[i] / jz;
        res[i] %= jz;
    }
    while(r){
        res[++tt] = r % jz;
        r /= jz;
    }
}

void div(int b){
    int r = 0;
    for(int i = tt; i >= 0; i -- ) {
        res[i] += r * jz;
        r = res[i] % b;
        res[i] /= b;
    } 
    while(!res[tt]) tt--;
}

void out(){
    printf("%lld",res[tt]);
    for(int i = tt - 1; i >= 0; i -- ){
        printf("%09lld",res[i]);
    }puts("");
}

signed main() {
    int n;
    cin >> n;
    init();
    for(int i = 0; i < cnt; i ++) {
        chu[p[i]] = ges(n * 2, p[i]) - ges(n, p[i]) * 2;
    }
    int k = n + 1;
    for(int i = 0;p[i]<=k; i++){
        int s = 0;
        while(k%p[i]==0){
            s ++;
            k/=p[i];
        }
        chu[p[i]] -= s;
    }
    res[0] = 1;
    tt =0 ;
    for(int i =2 ;i <= n*2; i++){
        for(int j = 0; j <chu[i];j++){
            mul(i);
        }
    }
    out();
    return 0;
}

以下重新整理到这里
单调栈
板子。

#include<cstdio>
#include<stack>
using namespace std;
#define ll long long
const int N = 1e5+5;
ll a[N];;
int L[N], R[N], st[N];
int main(){
    int n;
    while(~scanf("%d", &n) && n){
        for(int i = 1; i <= n; i++)
            scanf("%lld", &a[i]);
        int top = 0;
        for(int i = 1; i <= n; i++){
            while(top && a[st[top-1]] >= a[i]) top--;
            L[i] = (top==0) ? 0 : st[top-1];
            st[top++] = i;
        }
        
        top = 0;
        for(int i = n; i >= 1; i--){
            while(top && a[st[top-1]] >= a[i]) top--;
            R[i] = (top==0) ? (n+1) : st[top-1];
            st[top++] = i;
        }
        
        ll ans = 0;
        for(int i = 1; i <= n; i++)
            ans = max(ans, (ll)(R[i]-L[i]-1)*a[i]);
        
        printf("%lld\n", ans);
    }
    return 0;
}

队列

team queue
模拟队列
小组队列
开 2个队列A,B 在开个map什么的记录每个人属于什么队
第一个队列放map人对应属于什么
插入的时候 优先放到 B队列 如果A队列它不再了 插到A尾部 否在不管
pop 时候 如果这个第一队列空了 A弹出它 否在不管

#include <cstdio>
#include <string>
#include <iostream>
#include <map>
#include <queue>
using namespace std;
const int maxn = 1010;

int main (){
	int kase=0,t;
	queue<int> q,pq[maxn];
	map<int,int> team;
	char cmd[10];
	while(scanf("%d",&t)!=EOF&&t){
	while(!q.empty())q.pop();
		team.clear();
		printf("Scenario #%d\n",++kase);
		for(int i=0;i<t;i++){
			int n,x;
			scanf("%d",&n);
			while(n--){
				scanf("%d",&x);
				team[x]=i;
			}
			while(!pq[i].empty())pq[i].pop();
		}
		for(;;){
			int x;
			scanf("%s",cmd);
			if(cmd[0]=='S') {break;}
			if(cmd[0]=='E'){
				scanf("%d",&x);
				int t=team[x];
				if(pq[t].empty()) q.push(t);
				pq[t].push(x);
			}
			if(cmd[0]=='D'){
				int t=q.front();
				printf("%d\n",pq[t].front());
				pq[t].pop();
				if(pq[t].empty()) q.pop();
			}
		}
		cout<<endl;
	}
	return 0;
} 

蚯蚓

这题不太好想orz 首先是坎最长的 不需要考虑 但是每坎一次 其他都会长q 这样维护起来就变难了 所以 我们使用了一个detla
作为整体应该加的数据

每次选出最长的+detla 然后坎了它 因为被砍出得 这轮不会 在涨 所以 p1-q,p2-q; 放回队列 detla+=q;
这样就处理了其他量在变得情况

#include <bits/stdc++.h>
using namespace std;
#define int long long

const int maxn = 1e5 + 5 ;
const double pi = acos(-1.0);

int n, m, q, u, v, t;
priority_queue<int> que;
int a[maxn];

queue<int> q2, q3;

int rmax() {
    int x = -0x3f3f3f3f3f3f3f;
    if(!que.empty())
        x = max(x, que.top());
    if(!q2.empty())
        x = max(x, q2.front());
    if(!q3.empty())
        x = max(x, q3.front());
    if(!que.empty() && x == que.top())
        que.pop() ;
    else if(!q2.empty() && x == q2.front())
        q2.pop() ;
    else
        q3.pop() ;
    return x;
}

signed main() {
    cin >> n >> m >> q >> u >> v >> t;
    for(int i = 1; i <= n; i++)
        cin >> a[i], \
            que.push(a[i]);
    int res = 0;
    for(int i = 1; i <= m; i += 1) {
        int ma = rmax();
        ma += res;
        if(i%t==0) cout << ma << " ";
        int p1 = ma * u / v;
        int p2 = ma - p1;
        res += q;
        q2.push(p1 - res);
        q3.push(p2 - res);
    }
    cout << endl;
    for(int i=1;i<=n+m;i++) {
        int x = rmax();
        if(i%t==0)
            cout << x + res << " ";
    }
    cout << endl;
    return 0;
}

双端队列 待续

单调队列 最大字段和 这玩意应用挺多的 贪心要用 DP要用。。 经常需要它

#include <bits/stdc++.h>
using namespace std;
#define int long long

const int maxn = 3e5 + 5 ;
const double pi = acos(-1.0);

int n, m;
int a[maxn];
int sum[maxn];
int q[maxn], head, tail;

signed main() {
	cin >> n >> m ;
	for(int i = 1 ; i <= n ; i++) {
		cin >> a[i];
		sum[i] = sum[i - 1] + a[i];
	}
	q[1] = 0;
	int l = 1, r = 1;
	int ans = 0;
	for(int i = 1; i <= n; i++) {
		if(l <= r && q[l] < i - m)
			l++; // 队列>M
		ans = max(ans, sum[i] - sum[q[l]]);
		while(l <= r && sum[q[r]] >= sum[i])
			r--;
		q[++r] = i;
	}
	cout << ans << endl;
	return 0;
}
在 C++ 编程中,`0x3f3f3f3f` 是一个十六进制常量,其对应的二进制形式为 `00111111 00111111 00111111 00111111`,即每个字节的值为 `0x3f`。这种模式在实际编程中被广泛使用,尤其是在算法竞赛和内存初始化等场景中。 ### 十六进制值 `0x3f3f3f3f` 的常见用途 #### 1. 初始化数组或内存为一个“极大值” 在图论、动态规划等算法中,常常需要将数组初始化为一个非常大的值,表示“未访问”或“不可达”的状态。使用 `0x3f3f3f3f` 作为初始值的原因是它足够大(十进制为 1061109567),同时又不会导致整数溢出。例如: ```cpp #include <cstring> int dist[1000]; memset(dist, 0x3f, sizeof(dist)); // 将 dist 数组初始化为 0x3f3f3f3f ``` #### 2. 避免整数溢出问题 使用 `memset` 初始化数组时,如果直接使用 `INT_MAX`(定义在 `<climits>` 中),由于 `memset` 是按字节操作的,`INT_MAX` 的二进制形式为 `01111111 11111111 11111111 11111111`,每个字节分别为 `0x7f`,因此 `memset(arr, 0x7f, sizeof(arr))` 会将每个整数设置为 `0x7f7f7f7f`,这可能在某些情况下导致溢出问题。相比之下,`0x3f3f3f3f` 更加安全[^1]。 #### 3. 作为“无穷大”表示 在最短路径算法(如 Dijkstra 或 Floyd-Warshall)中,`0x3f3f3f3f` 常用于表示图中两个节点之间不可达的距离。例如: ```cpp const int INF = 0x3f3f3f3f; int graph[100][100]; for (int i = 0; i < 100; ++i) for (int j = 0; j < 100; ++j) graph[i][j] = INF; ``` #### 4. 内存填充与调试 在调试程序时,`0x3f3f3f3f` 也可以作为填充值,帮助识别未初始化的变量或内存区域。例如,在动态内存分配后,可以使用 `calloc` 或手动 `memset` 来填充此值,以确保后续访问时能发现未正确初始化的部分。 ### 示例代码 以下是一个使用 `0x3f3f3f3f` 表示无穷大的完整示例: ```cpp #include <iostream> #include <cstring> using namespace std; const int INF = 0x3f3f3f3f; const int MAXN = 100; int graph[MAXN][MAXN]; int n, m; void initGraph() { for (int i = 0; i < n; ++i) for (int j = 0; j < n; ++j) graph[i][j] = (i == j) ? 0 : INF; } void inputEdges() { cout << "Enter number of vertices and edges: "; cin >> n >> m; for (int i = 0; i < m; ++i) { int u, v, w; cin >> u >> v >> w; graph[u][v] = w; } } void printGraph() { for (int i = 0; i < n; ++i) { for (int j = 0; j < n; ++j) { if (graph[i][j] == INF) cout << "INF "; else cout << graph[i][j] << " "; } cout << endl; } } int main() { initGraph(); inputEdges(); printGraph(); return 0; } ``` ### 总结 `0x3f3f3f3f` 在 C++ 编程中是一个非常实用的常量,尤其适用于需要初始化数组、表示无穷大或调试内存的场景。它不仅在算法竞赛中广泛使用,也在实际开发中提供了一种高效且安全的编程技巧。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值