YbtOJ 「数据结构」 第1章 堆的应用

堆的应用

A. 【例题1】合并果子

每次将权值最小的前两个揪出来合并即可

#include <bits/stdc++.h>
#define pii pair < int , int >
using namespace std;
priority_queue < int , vector < int > , greater < int > > q;

int main()
{
	int n , temp , ans = 0;
	scanf ( "%d" , &n );
	for ( int i = 1 ; i <= n ; i ++ )
	{
		scanf ( "%d" , &temp );
		q.push(temp);
	}
	while ( q.size() > 1 )
	{
		int add = 0;
		add += q.top();q.pop();
		add += q.top();q.pop();
		ans += add;
		q.push(add);
	}
	printf ( "%d" , ans );
	return 0;
} 

B. 【例题2】序列合并

将两个数组排序 枚举第一个 并在第二个中从前到后判断 动态维护两个数组的和 如果当前这个加和已经大于堆的最大值就跳出循环 优化时间复杂度

最后倒序输出即可

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;

int read()
{
	int x = 0 , f = 1;
	char ch = getchar();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f;
}

priority_queue <int> q;

int n , a[N] , b[N] , ans[N];

signed main ()
{
	int n = read();
	for ( int i = 1 ; i <= n ; i ++ ) a[i] = read();
	for ( int i = 1 ; i <= n ; i ++ ) b[i] = read();	
	sort ( a + 1 , a + n + 1 );
	sort ( b + 1 , b + n + 1 );
	for ( int i = 1 ; i <= n ; i ++ )
		for ( int j = 1 ; j <= n ; j ++ )
		{
			int temp = a[i] + b[j];
			if ( q.size() < n ) q.push ( temp );
			else 
			{
				if ( q.top() > temp ) q.pop() , q.push ( temp );
				else break;
			}
		}
	for ( int i = n ; i ; i -- ) ans[i] = q.top() , q.pop();
	for ( int i = 1 ; i <= n ; i ++ ) printf ( "%d " , ans[i] );
	return 0;
}

C. 【例题3】龙珠游戏

维护一个链表 将所有的龙珠都读入之后 用值域从高到低枚举(因为龙珠的下标和值都是1n) 如果这个值所在的点有下一个点 那么输出这一对值并维护龙珠的相邻关系

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

const int N = 1e6 + 5;

int read()
{
	int x = 0 , f = 1;
	char ch = getchar();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f;
}

int n , ans , a[N] , nxt[N] , lst[N];

signed main ()
{
	n = read();
	for ( int i = 1 ; i <= n ; i ++ )
	{
		a[i] = read();
		nxt[a[i-1]] = a[i];
		lst[a[i]] = a[i-1];
	}
	for ( int i = n ; i >= 1 ; i -- )
	{
		if ( nxt[i] )
		{
			printf ( "%d %d " , i , nxt[i] );
			nxt[lst[i]] = nxt[nxt[i]];
			lst[nxt[nxt[i]]] = lst[i];
			nxt[nxt[i]] = 0;
		}
	}	
	return 0;
}

D. 【例题4】工作安排

反悔贪心 维护一个优先队列 优先队列大小为当前时间 权值为所有安排的事的利润大小

先按照时间排序 每次判断最低值和当前利润的大小关系 如果有更大利润则替换

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 5;

int read()
{
	int x = 0 , f = 1;
	char ch = getchar();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f;
}

priority_queue < int , vector<int> , greater<int> > q;

int n , ans;

struct node { int d , p; } a[N];

signed main ()
{
	n = read();
	for ( int i = 1 ; i <= n ; i ++ ) a[i].d = read() , a[i].p = read();
	sort ( a + 1 , a + n + 1 , [](const node a , const node b) { return a.d < b.d; } );
	for ( int i = 1 ; i <= n ; i ++ ) 
	{
		if ( q.size() < a[i].d ) q.push ( a[i].p ) , ans += a[i].p;
		else 
		{
			int temp = q.top();
			if ( temp < a[i].p )
			{
				ans -= temp;
				q.pop();
				ans += a[i].p;
				q.push ( a[i].p );
			}
		}
	}
	printf ( "%lld" , ans );
	return 0;
}

E. 1.家庭作业

贪心策略:先按照截止时间排序 每次先将这次的时间长度加上 如果超过了这次的合法时间 那么尝试用之前的a最大的事件来削减时间

如果这个时间削减至0了 那么寻找下一个a次小的时间 以此类推 直到时间合法

注意:如果这一次削减时间达到了阈值 那么现在的截止时间应该截止到现在这一个事件的截止时间 而不是这一个削减事件的截止时间

#include <bits/stdc++.h>
using namespace std;
#define int long long 
const int N = 1e6 + 5;

int read ()
{
	int x = 0 , f = 1;
	char ch = cin.get();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
	return x * f;
}

int n;
double tmp = 0 , ans = 0;

struct node
{
	double a , b , d;
	friend bool operator < ( node a , node b ) { return a.a < b.a; }
}a[N];

priority_queue<node> q;

signed main ()
{
	n = read();
	for ( int i = 1 ; i <= n ; i ++ ) cin >> a[i].a >> a[i].b >> a[i].d;
	sort ( a + 1 , a + n + 1 , [](const node a , const node b) { return a.d < b.d; } );
	for ( int i = 1 ; i <= n ; i ++ )
	{
		tmp += a[i].b;
		q.push ( a[i] );
		double delta = tmp - a[i].d;
		while ( delta > 0 )
		{
			node u = q.top(); q.pop();
			if ( u.b >= delta )//这一次够了
			{
				ans += delta / u.a;
				u.b -= delta;
				delta = 0;
				tmp = a[i].d;
				if ( u.b != 0 ) q.push(u);
			}
			else//这一次不够
			{
				ans += u.b / u.a;
				tmp -= u.b;
				delta -= u.b;
			}
		}
	}
	printf ( "%.2lf" , ans );
	return 0; 
}

F. 2.选数游戏

容易发现只有每一列被访问的行数有用 那么我们考虑贪心

从左向右枚举最后的列数 每遇到一列就将一整列直接加入堆 对于每一次加入的答案 我们都弹出数 直到队列中的数的个数小于等于k

然后再加上最后一次弹出的节点与剩余步数的乘积

#include <bits/stdc++.h>
using namespace std;
#define int long long 
#define endl '\n'
const int N = 1e6 + 5;

int read ()
{
	int x = 0 , f = 1;
	char ch = cin.get();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
	return x * f;
}

int n , m , k , sizee , sum , maxx , ans;

priority_queue < int , vector < int > , greater < int > > q;

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read() , m = read() , k = read();
	for ( int i = 1 ; i <= min ( m , k ) ; i ++ )//枚举最右面是哪一行
	{
		int temp = read();
		q.push ( temp );
		sizee += n , sum += n * temp;//将一整列加入堆
		while ( sizee > k && !q.empty() )
		{
			int u = q.top();
			q.pop();
			sizee -= n - 1 , sum -= ( n - 1 ) * u;
			maxx = u;
		}
		ans = max ( ans , sum + min ( n , k - sizee ) * maxx );
	}
	cout << ans << endl;
	return 0;
}

G. 3.内存管理

开一个优先队列q1记录空余的节点编号 开一个普通队列q2记录当前正在访问的节点编号

每次操作之前 先将所有不合法的节点全部扔出队列 再进行查询

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define fi first
#define se second
#define mkp make_pair
#define pii pair<int,int>
const int N = 1e6 + 5;

int read ()
{
	int x = 0 , f = 1;
	char ch = cin.get();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
	return x * f;
}

int n , vis[N];

priority_queue < int , vector<int> , greater<int> > q1;
int head = 1 , tail = 0 , x;
char op;
pii q2[N];//<编号,时间>

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	for ( int i = 1 ; i <= 30000 ; i ++ ) q1.push(i);
	while ( cin >> x >> op )
	{
		while ( head <= tail && q2[head].se + 600 <= x )
		{
			vis[q2[head].fi] --;
			if ( !vis[q2[head].fi] ) q1.push(q2[head].fi);
			head ++;
		}
		if ( op == '+' )
		{
			int u = q1.top(); q1.pop();
			vis[u] = 1 , q2[++tail] = mkp ( u , x );
			cout << u << endl;
		}
		else 
		{
			int y = read();
			if ( vis[y] )
			{
				cout << "+" << endl;
				vis[y] ++;
				q2[++tail] = mkp ( y , x );
			}
			else cout << "-" << endl;
		}
	}
	return 0;
}

H. 4.火车载客

九个月前的答辩代码 题解和新代码留坑

#include <bits/stdc++.h>
#define int long long
#define se second
#define fi first
#define pii pair < int , int >
using namespace std;
const int N = 6e5 + 5;
int n , k , c , tot , sum , ans;
struct train
{
	int st , end , num , id;//num个人,起始站点是st,结束站点是end 
	bool operator<(const train& a)const{return end<a.end;}
}a[N];
struct T
{
	int time , id , op;//time是下车或者上车时间,id是操作顺序 , op = 0是上车,op = 1是下车 
	bool operator < ( const T &a ) const { return time != a.time ? time < a.time : op > a.op; };
}b[N];
priority_queue < train > q;


signed main()
{
	scanf ( "%lld%lld%lld" , &k , &n , &c );
	for ( int i = 1 , s , t , p ; i <= k ; i ++ )
	{
		scanf ( "%lld%lld%lld" , &s , &t , &p );
		a[i] = (train) { s , t , p , 0 };
		b[++tot] = (T) { s , i , 0 };
		b[++tot] = (T) { t , i , 1 };
	}
	sort ( b + 1 , b + 1 + tot );
	for ( int i = 1 ; i <= tot ; i ++ )
	{
		int id = b[i].id;
		if ( b[i].op == 0 )//上车 
		{
			while ( !q.empty() && q.top().end < b[i].time )
			{
				a[q.top().id].id = 0;
				q.pop();
			}
			while ( sum < c && a[id].num )
			{
				int add = min ( c - sum , a[id].num );
				a[id].id += add;
				a[id].num -= add;
				q.push ( ( train ) { a[id].st , a[id].end , add , id } );
				sum += add; 
			}
			while ( !q.empty() && a[id].num && a[id] < q.top() )//最后面上车的人下车 
			{
				train temp = q.top() ; q.pop();
				int add = min ( a[id].num , temp.num );
				temp.num -= add;
				a[temp.id].id -= add;
				a[id].id += add;
				a[id].num -= add;
				if ( temp.num ) q.push ( temp );
				q.push ( ( train ) { a[id].st , a[id].end , add , id } );
			}
		}
		else//下车力 (喜)
		{
			ans += a[id].id;//达到目标的总人数 
			sum -= a[id].id;//当前车上人数 
			a[id].id = 0;
		}
	}
	printf ( "%lld" , ans );
	return 0;
} 


posted @   Echo_Long  阅读(179)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示