目录
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;
例题:
常规思路:
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())
如果找到
例题:
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
分析:
常规思路是开字符串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
分析:
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);
例题:
思路:
涉及到先后问题就是拓扑序,而这个的拓扑序不止一种,而且要求最后的和最大,贪心知道要使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:(小根堆+贪心)
代码(注释即思路):
#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;
}
本文参考: 国际大学生程序设计竞赛指南