基础数据结构 - 单调队列 单调栈 二叉树遍历 哈夫曼编码 堆 尺取法


注:使用时记得加上头文件和命名空间std

单调队列

这是链接

单调栈

这是链接

二叉树遍历

一、二叉树先中后序遍历之间的转换
题:

  1. 洛谷P1030 求先序排列
    由中序序列和后序序列求得前序序列,利用递归的方法,详见代码
void dfs(string s,string t){
	if(s.size()>0){
		char ch=t.back();
		cout<<ch;
		int i=s.find(ch);
		dfs(s.substr(0,i),t.substr(0,i));//左子树
		dfs(s.substr(i+1),t.substr(i,t.size()-i-1));//右子树,注意substr参数的大小
	}
	return ;
}

void solve(){
	cin>>ss[0]>>ss[1];
	dfs(ss[0],ss[1]);
	cout<<'\n';
	return ;
}
  1. hduBinary Tree Traversals
    由前序序列和中序序列求得后序序列,利用递归的方法,与上一个利用字符串的方法不同,但整体思路相同,详见代码
const int maxm=1e3+5,inf=0x3f3f3f3f,mod=998244353;
int n,a[maxm],b[maxm],c[maxm];

void get(int x1,int y1,int x2,int y2,int z){
	c[z]=a[x1];
	int pos=0;
	for(int i=x2;i<=y2;++i){
		if(b[i]==a[x1]){
			pos=i;
			break;
		}
	}
	int l=pos-x2,r=y2-pos;
	if(pos>x2){
		get(x1+1,x1+l,x2,pos-1,z-r-1);//函数参数再揣摩一下
	}
	if(pos<y2){
		get(x1+l+1,y1,pos+1,y2,z-1);//函数参数再揣摩一下
	}
	return ;
}

void solve(){
    while(cin>>n){
        char ch;
        for(int i=1;i<=n;++i){
            cin>>a[i];
        }
        for(int i=1;i<=n;++i){
            cin>>b[i];
        }
        get(1,n,1,n,n);
		for(int i=1;i<=n;++i){
			cout<<c[i]<<" \n"[i==n];
		}
    }
    return ;
}

signed main(){
    // ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    int _=1;
//    cin>>_;
    while(_--){
        solve();
    }
    return 0;
}
  1. P1229 遍历问题
    给定二叉树前序遍历和中序遍历结果,判断这样的二叉树中序遍历的可能情况
    要想前序遍历和后序遍历相等,那么在结点只有一个儿子的情况下才会在知道前序后序序列的情况下有不同的中序遍历,所以将题目转化成找只有一个儿子的节点个数。
string ss[2];
cin>>ss[0]>>ss[1];
ll ans=0;
for(int i=0;i<ss[0].size()-1;++i){
	for(int j=1;j<ss[1].size();++j){
		if(ss[0][i]==ss[1][j]&&ss[0][i+1]==ss[1][j-1])
			++ans;
	}
}
cout<<(1ll<<ans)<<'\n';

哈夫曼编码

题目:

  1. Entropy
    详见代码:
const int maxm=3e6+5,inf=0x3f3f3f3f,mod=1e4;

void solve(){
	priority_queue<ll,vector<ll>,greater<ll>> q;
	string ss;
	int ans[2];
	while(cin>>ss){
		if(ss=="END"){
			break;
		}
		ans[0]=8*ss.size();
		sort(ss.begin(),ss.end());
		int num=1;
		for(int i=1;i<ss.size();++i){
			if(ss[i]==ss[i-1]) ++num;
			else{
				q.push(num);
				num=1;
			}
		}
		q.push(num);
		ans[1]=0;
		if(q.size()==1){//此处为题目的一个陷阱:只有一种字符的情况!!!
			ans[1]=ss.size();
		}
		while(q.size()>1){
			int a,b;
			a=q.top();q.pop();
			b=q.top();q.pop();
			q.push(a+b);
			ans[1]+=a+b;
		}
		q.pop();
		double t=1.0*ans[0]/ans[1];
		cout<<ans[0]<<' '<<ans[1]<<' ';
		printf("%.1lf\n",t);
	}
	return ;
}

signed main(){
	// ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	int _=1;
//	cin>>_;
	while(_--){
		solve();
	}
	return 0;
}
  1. Safe Or Unsafe
    代码与上题类似,依旧是仅求哈夫曼编码的总编码长度

例题:

  1. 模板:P3378 【模板】堆
    可以自己手写一个堆,或者利用C++ STL priority_queue
    手写堆(菜勿怪)
const int maxm=1e6+5;
class minheap{
public:
	int size;
	int *data;
	minheap(int num=maxm){
		size=0;
		data=new int[num];
	}
	void push(int x){
		int id=++size;
		for(;id>1&&data[id/2]>x;id/=2){
			data[id]=data[id/2];
		}
		data[id]=x;
		return ;
	}
	void pop(){
		data[1]=data[size--];
		int child,t;
		for(int i=1;i*2<=size;i=child){
			child=i*2;
			if(child+1<=size&&data[child]>data[child+1])
				++child;
			if(data[i]<data[child]) break;
			t=data[i];
			data[i]=data[child];
			data[child]=t;
		}
		return ;
	}
	int top(){
		return data[1];
	}
}p;
  1. P1168 中位数

思路一:利用最大堆存小数,最小堆存大数,中途保证两堆元素个数差小于等于1,即可快速找到中位数

priority_queue<ll,vector<ll>,less<ll>> x;
priority_queue<ll,vector<ll>,greater<ll>> y;
void solve(){
	cin>>n;
	ll t;
	for(int i=1;i<=n;++i){
		cin>>a;
		if(x.size()==y.size()){
			x.push(a);
			if(y.size()){
				if(x.top()>y.top()){
					a=x.top();
					x.pop();
					y.push(a);
					a=y.top();
					y.pop();
					x.push(a);
				}
			}
		}else if(x.size()>y.size()){
			x.push(a);
			a=x.top();
			x.pop();
			y.push(a);
		}else{
			y.push(a);
			a=y.top();
			y.pop();
			x.push(a);
		}
		if(i%2){
			if(x.size()>y.size()) cout<<x.top()<<'\n';
			else cout<<y.top()<<'\n';
		}
	}
	return ;
}

思路二:直接vector二分插入实现元素有序

cin>>t;
a.insert(upper_bound(a.begin(),a.end(),t),t);
if(i%2) cout<<a[(i-1)/2]<<'\n';
  1. P2085 最小函数值
    暴力求解过不了时间,起码得优化一下
    方式一:考虑到二次函数的对称性,故所有的函数值都是从1开始为最小,那么就可以用一个f数组存x,来存储每一个abc的x的值(x从1开始递增)
const int maxm=1e4+5,inf=0x3f3f3f3f,mod=998244353;
ll n,m,a[maxm],b[maxm],c[maxm],f[maxm];

void solve(){
	cin>>n>>m;
	for(int i=0;i<n;++i){
		cin>>a[i]>>b[i]>>c[i];
		f[i]=1;
	}
	for(int i=0;i<m;++i){
		ll cmin=1e17,jmin,t;
		for(int j=0;j<n;++j){
			t=a[j]*f[j]*f[j]+b[j]*f[j]+c[j];
			if(t<cmin){
				cmin=t;
				jmin=j;
			}
		}
		cout<<cmin<<' ';
		f[jmin]++;
	}
	return ;
}

方式二:利用优先队列优化上述暴力判断最小的操作

const int maxm=1e4+5,inf=0x3f3f3f3f,mod=998244353;
ll n,m,a[maxm],b[maxm],c[maxm];
struct node{
	ll val,id,c;
	bool operator <(const node &a)const{
		return a.val<val;
	}
};
priority_queue<node> q;

void solve(){
	cin>>n>>m;
	node t;
	t.c=1;
	for(int i=0;i<n;++i){
		cin>>a[i]>>b[i]>>c[i];
		t.val=a[i]+b[i]+c[i];
		t.id=i;
		q.push(t);
	}
	for(int i=0;i<m;++i){
		t=q.top();
		q.pop();
		cout<<t.val<<" \n"[i==m-1];
		++t.c;
		t.val=a[t.id]*t.c*t.c+b[t.id]*t.c+c[t.id];
		q.push(t);
	}
	return ;
}
  1. P3045 Cow Coupons G
    贪心的思想+堆优化找寻的过程
    具体的思路可以看洛谷题解
    就是先把优惠券用到c最小的k个奶牛上,再考虑剩下的奶牛,原价便宜的先来,但此时存在一种情况,就是如果把优惠券换到新的奶牛身上,再补回当初奶牛的原价与优惠价的差,反而会更划算,那么这种情况更加符合题意。故就有了下面的代码:
//>>>Qiansui
#include<map>
#include<set>
#include<list>
#include<stack>
#include<cmath>
#include<queue>
#include<deque>
#include<cstdio>
#include<string>
#include<vector>
#include<utility>
#include<iomanip>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<functional>
#define ll long long
#define ull unsigned long long
#define mem(x,y) memset(x,y,sizeof(x))
//#define int long long
typedef std::pair<int,int> pii;
typedef std::pair<ll,ll> pll;
typedef std::pair<ull,ull> pull;

inline ll read()
{
	ll x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-48;ch=getchar();}
	return x*f;
}
using namespace std;

const int maxm=5e4+5,inf=0x3f3f3f3f,mod=998244353;
ll n,m,k,p[maxm],c[maxm];
bool vis[maxm];

priority_queue<pii,vector<pii>,greater<pii>> qp,qc;//存原价和打折后的价钱
priority_queue<int,vector<int>,greater<int>> qd;//存差价

void solve(){
	cin>>n>>k>>m;
	for(int i=0;i<n;++i){
		cin>>p[i]>>c[i];
		qp.push({p[i],i});
		qc.push({c[i],i});
	}
	for(int i=0;i<k;++i) qd.push(0);//先分k张优惠券,差价为0
	ll sum=0,tot=0;
	while(qp.size()){
		auto tp=qp.top();
		auto tc=qc.top();
		bool f=false;
		if(vis[tp.second]){
			qp.pop();
			f=true;
		}
		if(vis[tc.second]){
			qc.pop();
			f=true;
		}
		if(f) continue;
		if(tp.first<tc.first+qd.top()){//原价买比补差价便宜
			if(sum+tp.first>m) break;
			sum+=tp.first;
			vis[tp.second]=true;
			++tot;
			qp.pop();
		}else{//之前安排的优惠券现在用更便宜
			if(sum+tc.first+qd.top()>m) break;
			sum+=tc.first+qd.top();
			vis[tc.second]=true;
			++tot;
			qc.pop();
			qd.pop();
			qd.push(p[tc.second]-c[tc.second]);
		}
	}
	cout<<tot<<'\n';
	return ;
}

signed main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	int _=1;
//	cin>>_;
	while(_--){
		solve();
	}
	return 0;
}

尺取法

这是链接

posted on 2023-06-13 21:17  Qiansui  阅读(13)  评论(0编辑  收藏  举报