STL总结与一些练习题


STL(Standard Template Library) 提供三种类型的组件: 容器、迭代器和算法,它们都支持 泛型程序设计标准

容器主要有两类:顺序容器和关联容器。

  • 顺序容器(vector、list、deque 和 string 等)是一系列元素的有序集合。
  • 关联容器(set、multiset、map 和multimap)包含查找元素的键值。
  • 迭代器的作用是遍历容器。

c++数组find函数

int a[]={1,5,2,4,3};
int x=2;
int *index;
index=find(a,a+5,x);//在地址a和地址a+4之间查找,不包括a+5
cout<<index-a<<endl;//输出2

1.vector 向量容器

头文件:#include <vector>

默认第一个数是第0个元素(从 0 开始计数 )

begin()返回的是首元素位置的迭代器; end()返回的是最后一个元素的下一元素位置的迭代器。

使用 iterator 迭代器顺序遍历所有元素:

for(vector<int>::iterator it=v.begin();it!=v.end();it++){  
    cout<<*it<<" ";//输出迭代器当前位置上的元素值   
} 

这种迭代方法可简化为:for(auto i:v)cout<<i<<" ";

vector<int> v
vector<int> v(10):元素的下标为 0~9;另外,每个元素的值被初始化为0
vector<double> v(10,8.6)共有 10 个元素,每个元素的值是 8.6

v.push_back(...)

vector<...>::iterator it; //定义迭代器变量
for(it=v.begin();it!=v.end();it++) cout<<*it<<" "; //输出迭代器上的元素值

v.insert(v.begin()+0,8); 在最前面插入新元素,元素值为 8
v.insert(v.end(),3); 在向量末尾追加新元素 3
v.insert(v.begin()+2,1); 在第2个元素位置插入新元素 1(1是第三个数)

v.erase(v.begin()+2); 删除第2 个元素,从 0 开始计数
v.erase(v.begin()+1,v.begin()+5); 删除迭代器第 1 到第 5 区间的所有元素,从 0 开始计数,是删除4个数,因为左闭右开

v.clear(); 清空向量
v.empty()如果非空,则返回逻辑假,即 0,否则返回逻辑真,即 1
v.size()向量大小

#include <algorithm> reverse(v.begin(),v.end()); 反向排列向量的从首到尾间的元素
#include <algorithm> sort(v.begin(),v.end()); 默认升序

2.string 基本字符系列容器

头文件声明:#include <string>

string s;
s="hello,C++STL.";
string s; char ss[5000]; s=ss; 把整个字符数组赋值给 string 对象
s=s+'a'; s=s+"abc"; s.append("abc");

s.length()
s.empty()判断字符串是否为空

string::iterator it;   
it=s.begin(); //迭代器位置为字符串首 
s.insert(it+1,'p');//把字符'p'插入到第1个字符前(注意,字符位置是从0开始计数)  
string::iterator it=s.begin();//定义迭代器变量,指向字符串对象首元素    
s.erase(it+3);  cout<<s<<endl;//删除第 3 个元素(元素位置从 0 开始计数)    
s.erase(it,it+4);  cout<<s<<endl;//删除 0~3 区间的所有元素(还是左开右闭)   
s="";//清空字符串    
cout<<s.length()<<endl;//输出字符串的长度
s="abc123456";   
s.replace(3,3,"good");
从第3个开始,将连续的3个字符替换为“good”//即将“123”替换为“good” 
s="cat dog cat"; 
cout<<s.find('c')<<endl;//查找第一个字符‘c’,返回下标值:0  
cout<<s.find("cat")<<endl;//查找第一个子串“cat”,返回下标值:0 
cout<<s.find("dog")<<endl;//查找第一个子串“dog”,返回下标值:4
cout<<s.find("dogc")<<endl//查找第一个子串“dogc”,查不到则返回4294967295:4294967295
string s;  s="cat dog cat";    
cout<<s.compare("cat")<<endl;//s 比“cat”字符串大,返回 1    
cout<<s.compare("cat dog cat")<<endl;//s 与“cat dog cat”相等,返回 0    
cout<<s.compare("dog")<<endl;//s 比“dog”小,返回-1 

reverse(s.begin(),s.end()); 用 reverse 反向排序 string 对象
vector<string> v; 嵌套
string s; printf(s.c_str()); 用 printf 输出字符串对象,要采用 c_str()方法

sscanf函数:分离子串
char sa[100],sb[100],sc[100];    
sscanf("abc 123 pc","%s %s %s",sa,sb,sc);
//将字符串分离成子串,分隔符为空格//当用到数字的时候,跟 scanf 一样,它要传指针地址
int a,b,c;  sscanf("1 2 3","%d %d %d",&a,&b,&c);/将字符串分离成数字,分隔符为空格    
sscanf("4,5$6","%d,%d$%d",&x,&y,&z);//将字符串分离成数字,分隔符为“,”和“$”  

其他:string中的 reserve()和 resize()

3.set 集合容器

  • set 集合容器实现了红黑树(Red-Black Tree)的平衡二叉检索树的数据结构
  • 每个子树根节点的键值大于左子树所有节点的键值,而小于右子树所有节点的键值值;另外,还得确保根节点左子树的高度与右子树的高度相等,这样,二叉树的高度小,从而检索速度快
  • 它不会重复插入相同键值的元素,而采取忽略处理
  • 对于 set 容器中的键值,不可直接去修改,构造 set 集合的主要目的就是为了快速检索
  • 默认自动将元素按键值由小到大的顺序排列,当然,可以自定义比较规则函数

在这里插入图片描述
头文件声明:#include <set>

set<int> s;
s.insert(8); 如果是第一次,则可以插入
s.erase(6); 删除键值为 6 的那个元素
s.clear(); 清空集合
s.size()集合大小

set<int>::iterator it;定义前向迭代器
for(it=s.begin();it!=s.end();it++) cout<<*it<<" "; 中序遍历集合中的所有元素

set<int>::reverse_iterator rit;定义反向迭代器
for(rit=s.rbegin();rit!=s.rend();rit++) cout<<*rit<<" "; 反向遍历集合中的元素

使用 find() 方法对集合进行搜索,如果找到查找的键值,则返回该键值的迭代器位置, 否则,返回集合后一个元素后面的一个位置,即 end()

find()方法对集合进行搜索
set<int>::iterator it;//定义前向迭代器    
it=s.find(6);//查找键值为6的元素  
if(it!=s.end()) cout<<*it<<endl;//找到  
else cout<<"not find it"<<endl;//没找到     
自定义比较函数
struct myComp{  
    bool operator()(const int &a,const int &b){return a>b;}//{}不能省
};//自定义比较函数 myComp,重载“()”操作符 
set<int,myComp> s; 
set<int,myComp>::iterator it;//定义前向迭代器 
for(it=s.begin();it!=s.end();it++)cout<<*it<<" "; 
如果元素是结构体,那么,可以直接把比较函数写在结构体内
struct Info{  
    string name;float score;//重载“<”操作符,自定义排序规则  
    bool operator < (const Info &a) const{return score>a.score;}  
    //由大到小排列
}; 
set<Info> s; 
Info info; 
info.name="Jack";  
info.score=80.5;  
s.insert(info);
set<Info>::iterator it;//定义前向迭代器  
for(it=s.begin();it!=s.end();it++) cout<<(*it).name<<" : "<<(*it).score<<endl; 

例题:

7-21 求特殊方程的正整数解 (15分)

常规思路:

for(i=1;i<100;i++)
	for(j=1;j<100;j++)
		if(i*i+j*j==n&&i<j)

map解法:

#define rep2(i,a,b) for(int i = a; i <= b; ++i)

rep2(i,1,99)
	s.insert(i*i);
	
rep2(i,1,99)
	if(s.find(n-i*i)!=s.end() && i*i<(n+1)/2)

4.multiset 多重集合容器

  • multiset 与 set 唯一不同的是,multiset 允许重复的元素键值插入,而 set 则不允许
  • multiset 也需声明头文件包含“#include ”,由于它包含重复元素,所以,在插入元素、删除元素、查找元素上较 set 有差别。

在这里插入图片描述
头文件声明:#include <set>

multiset<string> ms;
ms.insert("abc");

multiset<string>::iterator it;
for(it=ms.begin();it!=ms.end();it++) cout<<*it<<endl;

int n=ms.erase("123"); 删除值为“123”的所有重复元素,返回删除元素总数

使用 find()方法查找元素,如果找到,则返回该元素的迭代器位置(如果该元素存在重复,则返回第一个元素重复元素的迭代器位置);如果没有找到,则返回end()迭代 器位置。

multiset<string>::iterator it; it=ms.find("123"); if(it!=ms.end()) 如果找到

例题:

L3-002 特殊堆栈 (30 分)

17分代码(multiset,3个测试点超时):

复杂度: O ( n 2 ) O(n^2) O(n2)

#include <bits/stdc++.h>
#define closeSync ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define debug(x) cout<<#x<<":"<<x<<endl;
#define f(i,a,n) for(int i=a;i<n;++i)
#define ff(i,a,n) for(int i=a;i<=n;++i)

const int INF=0x3f3f3f3f;
using namespace std;
typedef long long ll;

int n;
string s;
stack<int> sk;
multiset<int> st;
int main(){
	cin>>n;
	f(i,0,n){
		cin>>s;
		if(s=="Pop"){
			if(!sk.size())cout<<"Invalid"<<endl;
			else{
				int k=sk.top();sk.pop();
				cout<<k<<endl;
				int num=st.erase(k);
				f(i,0,num-1)st.insert(k);
			}
		}
		else if(s=="PeekMedian"){
			if(!sk.size())cout<<"Invalid"<<endl;
			else{
				int len=sk.size();
				multiset<int>::iterator it=st.begin();
				for(int i=0;i<len/2;i++)it++;
				if(len%2){
					cout<<*(it)<<endl;
				}
				else{
					it--;
					cout<<*(it)<<endl;
				}
			}
		}
		else{
			int t;
			cin>>t;
			sk.push(t);
			st.insert(t);
		}
	}
}

AC代码(vector结合upper_bound):

复杂度: O ( n l o g   n ) O(nlog\ n) O(nlog n)

#include <bits/stdc++.h>
#define closeSync ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define debug(x) cout<<#x<<":"<<x<<endl;
#define f(i,a,n) for(int i=a;i<n;++i)
#define ff(i,a,n) for(int i=a;i<=n;++i)
const int INF=0x3f3f3f3f;
using namespace std;
typedef long long ll;

int n;
string s;
vector<int> v1,v2;

int main(){
	vector<int>::iterator it;
	cin>>n;
	f(i,0,n){
		cin>>s;
		if(s=="Push"){
			int k;
			cin>>k;
			v1.push_back(k);
            //upper_bound(),查找第一个大于a的数字
			//也可以lower_bound()查找第一个大于或等于a的数字
			it=upper_bound(v2.begin(),v2.end(),k);
			v2.insert(it,k);
		}
		else if(s=="Pop"){
			if(!v1.size())cout<<"Invalid"<<endl;
			else{
				int num=v1[v1.size()-1];
				cout<<num<<endl;
				v1.pop_back();
				//不可以binary_search(),因为其返回bool值
				//不可以upper_bound(),因为其返回靠右的位置,会造成段错误
				it=lower_bound(v2.begin(),v2.end(),num);
				v2.erase(it);
			}
		}
		else if(s=="PeekMedian"){
			if(!v1.size())cout<<"Invalid"<<endl;
			else{
				int len=v1.size();
				cout<<v2[len/2+(len%2)-1]<<endl;
			}
		}
	}
}

5.map 映照容器

  • map 映照容器的元素数据是由一个键值和一个映照数据组成的,键值与映照数据之间具有一一映照的关系。
  • map 映照容器的数据结构也是采用红黑树来实现的,插入元素的键值不允许重复,比较函数只对元素的键值进行比较,元素的各项数据可通过键值检索出来。

在这里插入图片描述
头文件声明:#include <map>

map<string,float> m;
m["Jack"]=98.5;

输出键值与映照数据
map<string,float>::iterator it; 
for(it=m.begin();it!=m.end();it++) 
    cout<<(*it).first<<" : "<<(*it).second<<endl; 
自动输出
map<int,char> m; 
m[28]='k'; 
m[25]='m';//插入元素,按键值的由小到大放入黑白树中  
for(auto i:m)cout<<i.first<<" ";//输出:25 28

ps. 程序编译时,可能会产生代号为“warning C4786”的警告,“4786”是标记符超长警告的 代号。可以在程序的头文件包含代码的前面使用“#pragma warning(disable:4786)”宏语句, 强制编译器忽略该警告。4786 号警告对程序的正确性和运行并无影响。

删除元素:
map<int,char> m; 
m[28]='k'; 
m[25]='m';//插入元素,按键值的由小到大放入黑白树中 
m.erase(28);//删除键值为28的元素
反向遍历元素  
map<int,char>::reverse_iterator rit;  
for(rit=m.rbegin();rit!=m.rend();rit++){//输出键值与映照数据
    cout<<(*rit).first<<" : "<<(*rit).second<<endl;  
}    

使用 find()方法来搜索某个键值,如果搜索到了,则返回该键值所在的迭代器位置, 否则,返回 end()迭代器位置

map<int,char> m; 
m[28]='k'; 
m[25]='m';//插入元素,按键值的由小到大放入黑白树中 
map<int,char>::iterator it;  
it=m.find(28);  
if(it!=m.end()) {//搜索到该键值    
    cout<<(*it).first<<" : "<<(*it).second<<endl;  
}  
else{cout<<"not found it"<<endl; }

编写比较函数与 set 比较函数是一致的,因为它们的内部数据结构都是红黑树

自定义比较函数 myComp 
#pragma warning(disable:4786) 
struct myComp {  
    bool operator()(const int &a,const int &b)  {return a>b;} 
}; 
map<int,char,myComp> m; 
map<int,char,myComp>::iterator it;
 
直接把比较函数写在结构体内
struct Info {  
    string name;  
    float score;    
    bool operator < (const Info &a) const{return a.score<score;  }  
    //重载“<”操作符,自定义排序规则:按score由大到小排列
};
map<Info,int> m; 
Info info; 
info.name="Jack";  info.score=60;  
m[info]=25;
map<Info,int>::iterator it; 

例1

Shopping

分析:

常规思路是开字符串Hash数组实现 O ( N ) O(N) O(N)初始化, O ( 1 ) O(1) O(1)修改,但发现map O ( l o g N ) O(logN) O(logN)修改也可以水过,于是就用map了(注意这题有多组数据QAQ)

例2

String

分析:

map+字符串哈希即可

小技巧:

从第一位开始,每一个长度为m*l的子串都去判断。可以发现,从0开始的子串,和从l开始的子串,前者比后者多了子串[0, l),后者比前者多了子串[0 + m * l, 0 + m * l + l)。所以我们只需要预处理出,从位置0、1、2…l - 1开始的子串,后面的子串只需利用前面的结果即可。

6.multimap 多重映照容器

multimap与map唯独不同的是multimap允许插入重复键值的元素。由于允许重复键值存在,所以,multimap 的元素插入、删除、查找都与 map 不太相同

头文件声明:#include <map>

multimap<string,double> m; 
m.insert(pair<string,double>("Jack",300.5)); 
m.insert(pair<string,double>("Jack",306));//重复插入键值“Jack”  
m.erase("Jack");//删除等于某个键值的所有重复元素
multimap<string,double>::iterator it; 
it=m.find("Jack");// find()方法只返回重复键值中的第一个元素的迭代器位置  
if(it!=m.end())//找到 

7.deque 双端队列容器

  • deque与vector 几乎相同,采用线性表顺序存储结构。但与 vector 唯一不同的是deque采用分块的线性存储结构来存储数据,每块的大小一般为 512 字节,称为一 个deque块,所有的deque块使用一个 Map 块进行管理,每个 Map 数据项记录各个deque块的首地址

  • 这样一来,deque 块在头部和尾部都可插入和删除元素,而不需移动其他元 素(使用 push_back()方法在尾部插入元素,会扩张队列;而使用 push_front()方法在首部插 入元素和使用 insert()方法在中间插入元素,只是将原位置上的元素值覆盖,不会增加新元素)

  • 一般来说,当考虑到容器元素的内存分配策略和操作的性能时,deque 相对于 vector 更有优势。

在这里插入图片描述
头文件声明:#include <deque>

deque<float> d;
deque<int> d(10)
deque<int> d(10,8.5)

d.push_back(1); 从尾部连插入元素
d.push_front(10); 从头部插入元素(本质:不会增加新元素,只将原有的元素覆盖 )
d.insert(d.begin()+1,88); 从中间插入元素(本质:不会增加新元素,只将原有的元素覆盖 )

cout<<d[0]<<" "<<d[1]<<" "<<d[2]<<endl以数组方式输出元素
for(i=0;i<d.size();i++) cout<<d[i]<<" "; 以数组方式输出元素

以前向迭代器的方式遍历  
deque<int>::iterator it;  
for(it=d.begin();it!=d.end();it++){   
    cout<<*it<<" ";  
}  

以反向迭代器的方式遍历  
deque<int>::reverse_iterator rit;  
for(rit=d.rbegin();rit!=d.rend();rit++){   
    cout<<*rit<<" ";  
}  

d.pop_front(); 从头部删除元素
d.pop_back(); 从尾部删除元素
d.erase(d.begin()+1); 从中间删除元素
d.clear(); 清空元素
d.size()元素的个数

8.list 双向链表容器

list 容器实现了双向链表的数据结构,数据元素是通过链表指针串连成逻辑意义上的线性表,这样,对链表的任一位置的元素进行插入、删除和查找都是极快速的。
在这里插入图片描述
由于 list 对象的节点并不要求在一段连续的内存中,所以,对于迭代器,只能通过“++” 或“- -”的操作将迭代器移动到后继/前驱节点元素处。而不能对迭代器进行+n 或-n 的操 作,这点与vector等不同

头文件声明:#include <list>

list<int> l;
list<int> l(10);

push_back(2)往尾部插入新元素2
push_front(3)往首部插入新元素3
l.pop_front(); 删除首元素
l.pop_back(); 删除尾元素

list<int> l;//定义元素为整型的 list 对象,当前没有元素    
l.push_back(2);  //在链表尾部插入新元素,链表自动扩张   
l.push_front(8);//在链表头部插入新元素,链表自动扩张

list<int>::iterator it;  
it=l.begin();
it++;//注意,链表的迭代器只能进行++或--操作,而不能进行+n 操作  
l.insert(it,20);//在任意位置插入新元素,链表自动扩张
for(it=l.begin();it!=l.end();it++){cout<<*it<<" ";  }//正向遍历

list<int>::reverse_iterator rit;
for(rit=l.rbegin();rit!=l.rend();rit++){cout<<*rit<<" ";  }//反向遍历  

可以使用 remove()方法删除链表中一个元素,值相同的元素都会被删除:
l.remove(1); 删除值等于 1 的所有元素

l.erase(it); 使用 erase()方法删除迭代器位置上的元素。
l.clear();
cout<<l.size()

1.采用 find()查找算法可以在链表中查找元素,如果找到该元素,返回的是该元素的迭 代器位置;如果没有找到,则返回 end()迭代器位置。

2.find()算法需要声明头文件包含语句#include <algorithm>

list<int>::iterator it,it2;  
for(it=l.begin();it!=l.end();it++){cout<<*it<<" ";  } 
it=find(l.begin(),l.end(),5);//采用 find()查找算法在链表中查找  
if(it!=l.end())//如果找到  

l.sort(); 使用 sort()方法对链表排序,是升序排列
l.unique(); 采用 unique()方法可以剔除连续重复元素,只保留一个。

9.bitset 位集合容器

bitset 容器是一个bit位元素的序列容器,每个元素只占一个bit位,取值为 0 或 1,因 而很节省内存空间。下图是一个 bitset 的存储示意图,它的 10 个元素只使用了两个字节 的空间。
在这里插入图片描述

头文件声明:#include <bitset>

  • 创建 bitset 对象时,必须要指定容器的大小
  • bitset 对象的大小不能修改
  • 创建时所有元素的值默认为0

bitset<100000> b 创建 bitset 对象

b[0]=1; 下标法给元素赋值(第 0 位是低位,第 9 位是高位 )

b.set(); 采用 set()方法,一次性将元素设置为 1
b.set(pos,1); 采用 set(pos,1)方法,将pos位置元素设置为 1
b.set(pos,0); 采用 set(pos,0)方法,将pos位置元素设置为 0

b.count()b 中置为 1 的二进制位的个数
b.any() b 中是否存在置为 1 的二进制位?
b.size() b 中二进制位的个数

for(i=b.size()-1;i>=0;i--) cout<<b[i]; 输出
cout<<b<<endl; 直接向输出流输出全部元素(是逆向输出)

10.stack 堆栈容器

  • stack 堆栈是一个后进先出(Last In First Out,LIFO)的线性表
  • 插入元素的一端称为栈顶(Stack Top),而另一端则称为栈底(Stack Bottom)

在这里插入图片描述
头文件声明:#include <stack>

stack<int> s;
s.push(1); 元素入栈
s.pop()出栈(即删除栈顶元素)
cout<<s.top()<<endl; 读取栈顶元素
s.size()
s.empty()

11.queue 队列容器

  • queue 队列容器是一个先进先出(First In First Out,FIFO)的线性存储表
  • 元素的插入只能在队尾,元素的删除只能在队首

在这里插入图片描述
头文件声明:#include <queue>

queue<int> q;
q.size()
q.empty()
q.front()
q.back()
q.push(1);
q.pop();

12.priority_queue 优先队列容器

  • priority_queue 优先队列容器与队列类似,只能从队尾插入元素,从队首删除元素
  • 自动保持队列中的元素总是位于队首
  • 元素的比较规则默认为按元素的值由大到小排序;当然,可以重载“<”操作符来重新定义比较规则
  • 通常采用数据结构来实现

在这里插入图片描述
小根堆: 是一种经过排序的完全二叉树,其中任一非终端节点的数据值均不大于其左子节点和右子节点的值。

头文件声明:#include <queue> (与 queue 队列共 用一个头文件)

priority_queue<int> pq;
pq.push(1);   
pq.push(9);//入队
cout<<pq.size()<<endl;//返回队列中元素数目   
while(pq.empty()!=true){//所有元素出队,删除所有元素
    cout<<pq.top()<<" ";    
    pq.pop();
}

定义优先级的两种方法

在结构体中重载“<”操作符
struct Info{  
    string name;  float score;//重载“<”操作符,指定优先规则(排序规则)  
    bool operator < (const Info &a) const{//按 score 由小到大排列
        return a.score<score;  
    }  
};
priority_queue<Info> pq; 
Info info; 
info.name="Jack";  info.score=68.5;  
pq.push(info); 


重载“()”操作符来定义优先级
struct myComp{ //由小到大排列 
    bool operator()(const int &a,const int &b)  {return a>b;  } 
};  
priority_queue<int,vector<int>,myComp> pq; 
//定义优先队列,元素类型为 Info 结构体,显式说明内部结构是 vector
pq.push(1); 

例题:

Gym Class

思路:

涉及到先后问题就是拓扑序,而这个的拓扑序不止一种,而且要求最后的和最大,贪心知道要使ID值大的尽量排在前边,所以拓扑排序时用优先队列维护

代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
int in[100002];
long long ans;
int n;
vector<int>vec[100002];
void top(){
    priority_queue<int,vector<int>,less<int> >q;//等价于大根堆:priority_queue<int> q;
    for(int i=1;i<=n;i++){
        if(in[i]==0)
        q.push(i); 
    }
    int mina=1e6;
    while(!q.empty()){
        int now=q.top();
        mina=min(mina,now);//保证所求ID是最小的ID 
        ans+=mina;
        q.pop();
        for(int i=0;i<vec[now].size();i++){
            in[vec[now][i]]--;
            if(in[vec[now][i]]==0)q.push(vec[now][i]);
        }
    }
    printf("%lld\n",ans);
}
int main(){
    int t,m;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            vec[i].clear();
            in[i]=0;
        }
        for(int i=0;i<m;i++){
            int x,y;
            scanf("%d%d",&x,&y); 
            vec[x].push_back(y);
            in[y]++;
        } 
        ans=0;
        top();
    }
    return 0;
}
/*
思路:
优先队列保证拿出的是可以拿出的数据里面的最大值,而mina=min(mina,now)是保证所求ID是最小的ID
我们选择首先选择大的去,之后选择小的。如果存在关系的话,就先把没有关系的按照大到小去选择,
之后去掉相应关系,再把没有入过的边,在按照从大到小选择。
*/

13.pair 配对

pair的sort

#include<cstdio>
#include<map>
#include <algorithm>
using namespace std;
typedef pair<int,int> P;
P a[10];
int main(){
    for(int i=0;i<5;i++){
        scanf("%d%d",&a[i].first,&a[i].second);
    }
    sort(a,a+5);
    for(int i=0;i<5;i++){
        printf("%d %d\n",a[i].first,a[i].second);
    }
}
结论:按first从小到大排序 
输入: 
7 5
3 5
2 4
5 9
6 3
输出:
2 4
3 5
5 9
6 3
7 5

自定义cmp

bool cmp(pair<int, int>a, pair<int, int>b)
{
    return a.first<b.first;根据fisrt的值升序排序
    return a.second<b.second;根据second的值升序排序
}

pair插入数据(1,2):

  • q.push(make_pair(1,2));
  • q.push( {1,2} );

类比插入结构体数据(1,2):

struct edge{//存边结构体
	int u,v;
}; 
vector <edge> s;
s.push_back( (edge){1,2} );

例1:(小根堆+贪心)

Supermarket

代码(注释即思路):

#include <queue>
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 10006;
int n;
pair<int,int> a[N];
priority_queue<int> q;//用负数代表小根堆 
void Supermarket() {
    for (int i = 1; i <= n; i++)
        scanf("%d %d", &a[i].second, &a[i].first);//first为日期 
    sort(a + 1, a + n + 1);///贪心策略:按日期从小到大 
    for (int i = 1; i <= n; i++) {
        if (a[i].first == q.size() && -q.top() < a[i].second) {//比堆顶的利润高 
            q.pop();
            q.push(-a[i].second);
            continue;
        }
        if (a[i].first > q.size()) q.push(-a[i].second);//时间超额,直接放入堆里 
    }
    int ans = 0;
    while (q.size()){
        ans += q.top();
        q.pop();
    }
    cout << -ans << endl;
}
int main() {
    while (cin >> n) Supermarket();
    return 0;
}
/*
bool cmp(pair<int, int>a, pair<int, int>b){
    return a.first<b.first;//根据fisrt的值升序排序
    //return a.second<b.second;//根据second的值升序排序
}
策略:
先按时间从长到短排序,之后堆顶放利润低的,这样便于动态替换时
一定先把利润低的顶替掉 
*/

例2:(LIS)

友好城市

代码:

#include <iostream>
#include <algorithm>
using namespace std;
typedef pair<int, int> PII;
const int N = 5010;
int n;
PII city[N];
int f[N];
int main(){
    scanf("%d", &n);
    for (int i = 0; i < n; i ++ )
		scanf("%d%d", &city[i].first, &city[i].second);
    sort(city, city + n);//按first从小到大排序
    int res = 0;
    fill(f,f+n,1);
    for (int i = 0; i < n; i ++ ){
        for (int j = 0; j < i; j ++ )
            if (city[i].second > city[j].second)
				f[i] = max(f[i], f[j] + 1);
        res = max(res, f[i]);
    }
    printf("%d\n", res);
    return 0;
}

本文参考: 国际大学生程序设计竞赛指南

  • 5
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值