算法竞赛入门经典五——C++与STL入门

  • 排序与二分查找

二分查找的一个误区。。。以前一直忽视,直到有天我在这本书上看到了,但还是不以为意。然鹅后果来了,一道题调了好久
像这个代码,其实只能查找a1 ~ a4区间内部的0,仔细分析代码,l最多为4,r最多为5,因此最多只能查到a4。执行下面代码最后的返回值l还会是5,但这纯属一个巧合,因为程序在a1~ a4查不到0,l自动跳到了5。如果把a5这个元素改成6,程序还是会输出5.正确是做法是把r初始化为6,即查找最大下标的下一位,这样查到了就会返回5,查不到返回6.
STL模板库函数也是这样的,binary_serach(num,num+n,x)会查找下标为0 ~ n-1的值有没有x,有的话返回true,否则返回false。这个函数不是很实用,因为要用就要把所有的值算出来排到数组里,而往往我们并不需要计算所有值,而只需计算出查找路径上的值。

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int a[6]={0,-8,-1,-1,-1,0};
	int l=1,r=5,m;
	while(r>l)
	{
		m=l+(r-l)/2;
		if(a[m]==0)r=m;
		else l=m+1;
		cout<<l<<" "<<r<<endl;
	}
	cout<<l;
}

下确界:lower_bound查找x的下确界,返回一个指针位置p,使得起始位置到p(左闭右开)的元素全部小于x。如果查找不到,返回最后一个元素后面的位置。具体用法如下:
int *p=lower_bound(num,num+n,x);
能查找从a0开始长度为n即从a0 ~an-1的元素的下确界,如果x>an-1,会返回an的指针。

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int a[6]={-8,-8,-1,-1,0,6};
	int x;
	while(cin>>x)
	{
		int *p=lower_bound(a,a+5,x);
		cout<<*p<<endl;
	}
}

输入2或6,都会返回6
上面的代码如果改一下,将a+5改成a+6,就会查找a0 ~a5的下确界,因此如果输入2 3 4 5 6,都会返回6的位置,如果输入7则会返回6的下一个位置。
有时需要自己写代码实现lower_bound

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int a[6]={-8,-8,-1,-1,0,6};
	int x,l=0,r=5;
	while(r>l)
	{
		m=l+(r-l)/2;
		if(a[m]>=x)r=m;
		else l=m+1;
	}
	cout<<l;
}

此代码与上面等价,如果将r初始化为6,则与lower_bound(a,a+6,x);等价
上确界:upper_bound查找上确界,它与lower_bound不同的是,upper_bound(a,a+5,x)从p到a4,左右都是闭区间的值都要大于x

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int a[6]={-8,-1,0,2,3,6};
	int x;
	while(cin>>x)
	{
		int *p=upper_bound(a,a+5,x);
		cout<<*p<<endl;
	}
}

与lower_bound类似,此代码输入3 4 5 6 7 8都会返回6的位置,如果将a+5改成a+6,输入3 4 5会返回6的位置,输入 6 7 8会返回6的下一个位置。upper_bound的手动实现只需要将lower_bound代码中的大于等于改成大于

  • 矩形分割
    查看 提交 统计 提问
    总时间限制: 1000ms 内存限制: 65536kB
    描述
    平面上有一个大矩形,其左下角坐标(0,0),右上角坐标(R,R)。大矩形内部包含一些小矩形,小矩形都平行于坐标轴且互不重叠。所有矩形的顶点都是整点。要求画一根平行于y轴的直线x=k(k是整数) ,使得这些小矩形落在直线左边的面积必须大于等于落在右边的面积,且两边面积之差最小。并且,要使得大矩形在直线左边的的面积尽可能大。注意:若直线穿过一个小矩形,将会把它切成两个部分,分属左右两侧。

输入
第一行是整数R,表示大矩形的右上角坐标是(R,R) (1 <= R <= 1,000,000)。
接下来的一行是整数N,表示一共有N个小矩形(0 < N <= 10000)。
再接下来有N 行。每行有4个整数,L,T, W 和 H, 表示有一个小矩形的左上角坐标是(L,T),宽度是W,高度是H (0<=L,T <= R, 0 < W,H <= R). 小矩形不会有位于大矩形之外的部分。
输出
输出整数n,表示答案应该是直线 x=n。 如果必要的话,x=R也可以是答案。
样例输入
1000
2
1 1 2 1
5 1 2 1
样例输出
5

#include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
int r,n;
ll sums=0;
struct square//矩形结构体
{
	ll l,t,w,h;
};
square sq[11000];
ll f(int x)//计算左右面积差
{
	int i;
	ll sum=0;//sum为左边小矩形总面积 
	for(i=1;i<=n;i++)
	{
		if(sq[i].l+sq[i].w<=x)sum+=sq[i].h*sq[i].w;//小矩形全部落在左边 
		else if(sq[i].l<=x)sum+=sq[i].h*(x-sq[i].l);//小矩形被分割
	}
	return 2*sum-sums;//左-右 
}
int find()//二分查找0的下确界
{
	int left=0,right=r,middle;
	//这里有个隐患。在查找0的下确界时,我把right初始化为r,是不能找到f(r)的,应该初始化为r+1。
	//但是这题在x等于r时,f(r)不可能为负数,这样就保证了查找后的下标最大为r,因为就算f(r-1)为负,f(r)为正,也只能插入到r的位置。
	//但是如果f(r)可能为负,就要小心了:如果将right初始化为r,只要f(r-1)为负,不管f(r)为正还是为负,程序总会插到r的位置--因为只能查找到r-1的位置。
	//然而如果f(r)为负,正确做法应该是将0插入到r+1的位置。而加入把right初始化为r+1,
	//那么就能查询到下标为r的位置,此时如果f(r-1)为负,f(r)为正或0,程序会插入到r的位置,如果f(r)也为负,
	//那么程序就会做出正确的选择--将0插入到r+1的位置
	while(right>left)
	{
		middle=left+(right-left)/2;
		if(f(middle)>=0)right=middle;
		else left=middle+1;
	}
	while(f(left)==f(left+1)&&left<r)//由于程序要求左边的大矩形面积最大,如果left的右边根本没有小矩形,
	//那么不妨将left向右移动--此时不改变f(left)的值,然而可以使左边的大矩形变得更大。
	left++;
	return left;
}
int main()
{
	int i;
	cin>>r>>n;
	for(i=1;i<=n;i++)
	{
		cin>>sq[i].l>>sq[i].t>>sq[i].w>>sq[i].h;
		sums+=sq[i].h*sq[i].w;//所有小矩形总面积 
		//注意不仅sums要long long型,h和w也要long long型,因为要先计算h*w然后再赋值。
	}
	cout<<find();
}
  • 向量vector
    vector与数组类似,也可以用下标来访问元素,即v[i]。只不过vector可以动态的改变大小,数组一旦申请好大小就不能再改变了。vector作为一种容器,也可以用迭代器来访问,但是比较麻烦。
    vector vec; //声明一个int型向量
    vector vec(5); //声明一个初始大小为5的int向量
    vector vec(10, 1); //声明一个初始大小为10且值都是1的向量
    vector vec(tmp); //声明并用tmp向量初始化vec向量
    vector tmp(vec.begin(), vec.begin() + 3); //用向量vec的第0个到第2个值初始化tmp
    int arr[5] = {1, 2, 3, 4, 5};
    vector vec(arr, arr + 5); //将arr数组的元素用于初始化vec向量
    vector vec(&arr[1], &arr[4]); //将arr[1]~arr[4]范围内的元素作为vec的初始值
    两个相同类型的向量也可以直接赋值
    vec1=vec2;
    向量大小: vec.size();
    更改向量大小为a: vec.resize(a);//可以在添加或删除元素后使用
    向量判空: vec.empty();
    多个元素赋值: vec.assign(); //类似于初始化时用数组进行赋值
    末尾添加元素: vec.push_back();
    末尾删除元素: vec.pop_back();
    任意位置插入元素: vec.insert(p,8);
    任意位置删除元素: vec.erase§;//这两个只能用迭代器访问
    交换两个向量vec和vec2的元素: vec.swap(vec2);
    与swap(vec,vec2);的效果相同
    清空向量元素: vec.clear();
    下标访问: vec[1]; //并不会检查是否越界
    at方法访问: vec.at(1); //以上两者的区别就是at会检查是否越界,是则抛出out of range异常
    访问第一个元素: vec.front();
    访问最后一个元素: vec.back();
  • 木块问题
    在这里插入图片描述
#include<bits/stdc++.h>
using namespace std;
int n;
vector<int> v[30]; 
void pos(int x,int &a,int &b)//给出x计算x在哪个向量上面,以及在向量上的位置 
{
	int i,j;
	for(i=0;i<n;i++)
	for(j=0;j<v[i].size();j++)
	if(v[i][j]==x)
	{
		a=i;
		b=j;
		return;
	}
}
void del(int x,int y)//给定向量x,将x中y后面的所有元素 归位
{
	int i;
	for(i=y+1;i<v[x].size();i++)
	v[v[x][i]].push_back(v[x][i]);
	v[x].resize(y+1);
}
void move(int a,int b,int c)//将向量a的b及后面的元素加到向量c后
{
	int i;
	for(i=b;i<v[a].size();i++)
	v[c].push_back(v[a][i]);
	v[a].resize(b);
} 
int main()
{
	int c,i,j,a,b;
	string s1,s2;
	cin>>n;
	for(i=0;i<n;i++)
	v[i].push_back(i);//初始化 
	while(cin>>s1&&s1!="quit"&&cin>>a>>s2>>b)
	{
		int va,pa,vb,pb;//a的向量和位置 ,b的向量和位置  
		//检验合法否
		pos(a,va,pa);
		pos(b,vb,pb);
		if(va==vb)continue;//a和b在同一向量
		if(s1[0]=='m')del(va,pa);//move
		if(s2[1]=='n')del(vb,pb);//onto
		move(va,pa,vb);
	}
	for(i=0;i<n;i++)
	{
		cout<<i<<":";
		for(j=0;j<v[i].size();j++)
		cout<<" "<<v[i][j];
		cout<<endl;
	}
} 
  • multiset容器
    multiset内部是平衡二叉树结构,适合于存储需要大量查找,删除,插入操作的数据的数据结构。multiset按照从小到大排序,当然也可以自定义排序类型,允许插入重复值。
#include <bits/stdc++.h> 
using namespace std;
int main()
{
	multiset<int> st;
	int a[10]={4,7,15,7,9,3,5,26,15,19};
	for(int i=0;i<10;i++)
	st.insert(a[i]);//a[i]并没有放到multiset里面,是复制品放到里面了 
	multiset<int>::iterator i;//访问数据需要迭代器 
	for(i=st.begin();i!=st.end();i++)//迭代器不能比较大小
	//st.end()指向的是容器最后一个元素后面一位的位置 
	cout<<*i<<" ";//迭代器类似于指针,需要取地址访问 
	cout<<endl;
	i=st.find(22);//查找22的值,找不到返回的是st.end()
	if(i==st.end())cout<<"cant find 22"<<endl;
	else cout<<*i<<endl;
	st.insert(22);
	i=st.find(22);
	if(i==st.end())cout<<"cant find 22"<<endl;
	else cout<<*i<<endl;
	i=st.lower_bound(15);
	//下确界,可以理解为从左边逼近,使得从开始到i的值(不包括i)都在15前面
	cout<<*(--i)<<endl;
	i=st.upper_bound(19);
	//上确界,可以理解为从右边逼近,使得从结束到i的值(包括i)都在19后面
	cout<<*(i)<<endl;
	st.erase(i);//删除i位置的值 
	for(i=st.begin();i!=st.end();i++)
	cout<<*i<<" ";
	cout<<endl;
}

在这里插入图片描述
按照从大到小排序或自定义排序规则

#include <bits/stdc++.h> 
using namespace std;
struct rule
{
	//自定义的排序比较规则,按照个位从小到大排序,注意要放到结构体里面,前面要有const
	bool operator () (const int &a,const int &b) 
	{
		return (a%10<b%10);
	}
};
int main()
{
	multiset<int,greater<int> > st;//注意这两个>>之间需要一个空格,不然编译器当成流输入了。 
	int a[10]={1,14,12,13,7,13,21,19,8,8};
	for(int i;i<10;i++)
	st.insert(a[i]);
	multiset<int,greater<int> >::iterator i;
	for(i=st.begin();i!=st.end();i++)
	cout<<*i<<" ";
	cout<<endl;
	//再定义一个自定义排序规则的multiset
	multiset<int,rule> st2;
	for(int j=0;j<10;j++)
	st2.insert(a[j]);
	multiset<int,rule>::iterator p;
	for(p=st2.begin();p!=st2.end();p++)
	cout<<*p<<" ";
	cout<<endl;
	//查找相等并不是所有元素相等,而是参与排序的所有元素相等。或者是说x必须排在y前面和y必须排在x前面都不成立,则认为x与y相等
	p=st2.find(883); 
	if(p!=st2.end())cout<<*p;
}

在这里插入图片描述
自定义结构体后插入容器,此时一定要自定义排序规则,否则会出错

#include <bits/stdc++.h> 
using namespace std;
struct student//自定义的student数据类型 
{
	char name[20];
	int id;
	int score;
 };
struct rule
{
	//自定义的排序比较规则,按照成绩从大到小排序,成绩相同的按照姓名字典序排序 
	bool operator () (const student &a,const student &b) 
	{
		if(a.score!=b.score)return (a.score>b.score);
		else return strcmp(a.name,b.name)<0;
	}
};
student s[]={{"Jack",112,78},{"Mary",102,85},{"Ala",333,92},{"Zero",101,70},{"Cindy",102,78}};
int main()
{
	multiset<student,rule> st;//注意这两个>>之间需要一个空格,不然编译器当成流输入了。 
	int l=sizeof(s)/sizeof(student);
	for(int i=0;i<l;i++)
	st.insert(s[i]);
	multiset<student,rule>::iterator i;
	for(i=st.begin();i!=st.end();i++)//i相当于指针,用指针访问结构体的值需要用箭头 
	cout<<i->name<<" "<<i->id<<" "<<i->score<<endl;
	student sb={"Ala",88,92};//新学生,分数92与Ala相同 ,但是id不同 
	i=st.find(sb);//由于查找之查找排序相关的值,即查找92分姓名为Ala的,所以会输出Ala,尽管id不一样 
	if(i!=st.end())cout<<endl<<i->name<<" "<<i->id<<" "<<i->score;
}

在这里插入图片描述

  • set容器
    set与multiset的不同之处就在于set中不能有重复元素,所谓重复是指x,y谁在前谁在后都无所谓,就说x和y重复。或者理解为参与排序的值相同就叫重复,因此set插入元素可能不成功
    set();
    产生一个空的set/multiset,不含任何元素
    set c(op)
    以op为排序准则,产生一个空的set/multiset
    set c1(c2)
    产生set c2的副本,所有元素都被拷贝
    set c(beg,end)
    以区间[beg,end)内的所有元素产生一个set/multiset
    c.size()
    返回当前的元素数量
    c.empty ()
    判断大小是否为零,等同于0 == size(),效率更高
    c.max_size()
    返回能容纳的元素最大数量
    c1 == c2
    判断c1是否等于c2
    count (elem)
    返回元素值为elem的个数
    find(elem)
    返回元素值为elem的第一个元素,如果没有返回end()
    lower _bound(elem)
    返回元素值为elem的第一个可安插位置,也就是元素值 >= elem的第一个元素位置
    upper _bound (elem)
    返回元素值为elem的最后一个可安插位置,也就是元素值 > elem 的第一个元素位置
    equal_range (elem)
    返回elem可安插的第一个位置和最后一个位置,也就是元素值==elem的区间
    c1 = c2
    将c2的元素全部给c1
    c1.swap(c2)
    将c1和c2 的元素互换
    swap(c1,c2)
    同上,全局函数
    c.insert(elem)
    插入一个elem副本,返回新元素位置,无论插入成功与否。
    c.insert(pos, elem)
    安插一个elem元素副本,返回新元素位置,pos为收索起点,提升插入速度。
    c.insert(beg,end)
    将区间[beg,end)所有的元素安插到c,无返回值。
    c.erase(elem)
    删除与elem相等的所有元素,返回被移除的元素个数。
    c.erase(pos)
    移除迭代器pos所指位置元素,无返回值。
    c.erase(beg,end)
    移除区间[beg,end)所有元素,无返回值。
    c.clear()
    移除所有元素,将容器清空
#include <bits/stdc++.h> 
using namespace std;
int main()
{
	set<int> st;
	int a[10]={4,7,15,7,9,3,5,19,15,19};
	for(int i=0;i<10;i++)
	st.insert(a[i]);//insert是有返回值的,插入成功返回迭代器,失败返回布尔类型 
	cout<<st.size()<<endl;
	set<int>::iterator i;
	for(i=st.begin();i!=st.end();i++)
	cout<<*i<<" ";
	cout<<endl;
	pair<set<int>::iterator,bool> result=st.insert(3);
	//pair相当于两个类型合在一起,pair<类型1,类型2> 名字  就相当于:
	//struct 名字 
	//{
	//	类型1 first;
	//	类型2 second; 
	//}; 
	if(result.second)cout<<"insert sussessfully"<<endl;
	else cout<<*result.first<<"already existed"<<endl;
}

在这里插入图片描述

  • 安迪的第一个字典
    在这里插入图片描述
    这题虽然简单,但包含很多有用的东西。首先,如何从一堆带空格和标点的句子中分离得到一个个单词?可以采用cin读入,这样不会读入空格,每次可以得到一个单词。(注意这样是默认就算带有标点,单词和单词之间也有空格。比如:hello, asce 这里的asce前应有一个空格。)这样就得到了每个带标点的单词,然后对于单词的每一位,是字母则转小写,是标点就转换为空格,这样可以利用流读入不会读入空格的特点来分离标点。最后需要对单词进行去重和排序,正好符合set的特点。
#include<bits/stdc++.h>
using namespace std;
int n;
string s;
int main()
{
	set<string> se;
	int i=0,l=0;
	while(cin>>s)
	{
		for(i=0;i<s.length();i++)
		if(!isalpha(s[i]))s[i]=' ';//判断是不是字母的函数 tolower(char c)也可以将一个字母转换成小写 
		transform(s.begin(),s.end(),s.begin(),::tolower);//大小写转换函数 
		stringstream ss(s);//把 
		ss>>s;
		se.insert(s);
		l++;
	}
	set<string>::iterator p;
	for(p=se.begin();p!=se.end();p++)
	cout<<*p<<endl;
} 
  • multimap容器
    在这里插入图片描述
    在这里插入图片描述
    由于需要根据分数查询姓名和学号,应把分数设置为key,姓名和学号合成一个结构体,设置为value。由于key值不唯一,应用multimap。在输入的时候是三个信息一起输入,就设置两重结构体吧。multimap不能用下标。
    add jack 12 78
    query 78
    query 81
    add percy 9 81
    add marry 8 81
    query 82
    add tom 11 79
    query 80
    query 81
#include<bits/stdc++.h>
using namespace std;
int n;
string s;
struct studentinfo
{
 	char name[20];
 	int id;
};
struct student
{
	int score;
	studentinfo info;
};
int main()
{
	typedef multimap<int,studentinfo> std_map;
	std_map mp;
	student stu;
	char cmd[20];
	while(cin>>cmd)
	{
		if(cmd[0]=='a')
		{
			cin>>stu.info.name>>stu.info.id>>stu.score;
			mp.insert(make_pair(stu.score,stu.info));
			//make_pair生成一个pair变量。注意不能直接赋值,因为multimap会覆盖 
		}
		if(cmd[0]=='q')
		{
			int score;
			cin>>score;
			std_map::iterator p;
			p=mp.lower_bound(score);
			if(p==mp.begin())
			{
				cout<<"Nobody"<<endl;
				continue; 
			}
			//查询失败返回mp.begin()的值 
			//如果查到多个值怎么处理?由于只有一个返回值,我们要遍历所有分数相同的学生,输出学号最大的那个
			char taname[20];//目标学生的姓名和id 
			int taid,max=0; 
			p--;
			score=p->first;
			//由于p是pair形指针,访问元素只能是first,second 
			for(;p!=mp.begin()&&p->first==score;p--)
			if(p->second.id>max)//p->second就属于结构体类型了,再访问要用.而不是-> 
			{
				taid=p->second.id;
				strcpy(taname,p->second.name);
				max=p->second.id;
			}
			//如果是因为走到了mp.begin()而终止循环,那么就没有考虑第一个元素,需要对此元素进行处理 
			if(p==mp.begin()&&p->first==score&&p->second.id>max)
			{
				taid=p->second.id;
				strcpy(taname,p->second.name);
			}
			cout<<taname<<" "<<taid<<" "<<score<<endl;
		 } 
	}
} 
  • map
    map的操作与multimap类似,只不过里面没有重复的key值。可以用下标访问或插入,用insert插入可能失败,用下标的话如果key值存在会修改key值对应的value值。insert的迭代器p的类型为原map迭代器类型和bool类型的复合,即插入成功first为迭代器位置,second为1,表示插入成功,second为0表示插入失败。
    //用insert函数插入pair
    mapStudent.insert(pair<string, string>(“r000”, “student_zero”));
    //用"array"方式插入
    mapStudent[“r123”] = “student_first”;
    mapStudent[“r456”] = “student_second”;
    查找
    iter = mapStudent.find(“r123”);
    if(iter != mapStudent.end())
    cout<<“Find, the value is”<second<<endl;
    else
    cout<<“Do not Find”<<endl;
#include<bits/stdc++.h>
using namespace std;
struct student
{
	string name;
	int score;
};
int main()
{
	typedef map<string,int> std_map;
	std_map mp;
	student stu[5]={{"jack",89},{"tom",74},{"cindy",87},{"alysa",87},{"michael",98}};
	for(int i=0;i<5;i++)//插入
	mp.insert(make_pair(stu[i].name,stu[i].score));
	mp["jack"]=77;//修改jack的分数为77 
	//全部打印 注意p是pair的指针类型 ,全部数据是按照name排序 
	for(std_map::iterator p=mp.begin();p!=mp.end();p++)
	cout<<p->first<<" "<<p->second<<endl;
	//用insert插入新值,看看能不能成功呢
	student st={"jack",99};
	//注意p的类型,超级特殊,迭代器和布尔的杂交体 
	pair<std_map::iterator,bool>p=mp.insert(make_pair(st.name,st.score));
	if(p.second==0)//表示插入失败
	cout<<"insert failed!"<<endl;
	else cout<<p.first->first<<" "<<p.first->second<<"inserted"<<endl;
	mp["asce"]=100;//插入新值
	for(std_map::iterator p=mp.begin();p!=mp.end();p++)
	cout<<p->first<<" "<<p->second<<endl;
}

单词词频统计排序
在这里插入图片描述
这题呀,先用map<string,int>统计每个单词的个数。具体做法是,map中不存在某单词时,对应的value值为0,每次输入单词s的时候把mp[s]的值加1即可统计出单词的个数,放在value值里面。但是排序要求对value的值排序,但是map是不能排序value的,因此只能把所有的map序列组成一个结构体(包括单词以及单词出现次数)放进一个set里面,并自定义规则排序。

#include<bits/stdc++.h>
using namespace std;
string s;
struct word//单词结构体 
{
	string s;
	int num;
};
struct rule//set的排序规则 
{
	bool operator()(const word &a,const word &b)
	{
		if(a.num!=b.num)return a.num>b.num;
		else return a.s<b.s;
	}
};
typedef map<string,int> std_map;
std_map mp;
set<word,rule> se;//word结构体组成的set 
int main()
{
	word w;
	while(cin>>s)
	mp[s]++;//统计好了单词个数 
	for(std_map::iterator p=mp.begin();p!=mp.end();p++)
	{
		w.s=p->first;
		w.num=p->second;
		se.insert(w);//把结构体压入set 
	}
	for(set<word,rule>::iterator p=se.begin();p!=se.end();p++)
	cout<<p->s<<" "<<p->num<<endl;//set已按规则排序,输出即可 
}
  • 反片语
    在这里插入图片描述
    这个题啊,首先看单词的要求,既然可以改变顺序,那么顺序就不重要,那么就都按照一个标准序计算吧,即把string排个序,然后再转个小写,就结了。那么程序要求输出没有重复的单词,那我们需要在map中统计一下单词的个数,然后只要个数为1的。当然,用count函数可以返回key值的个数,对于map来说只能是0或1了。最终数据输入完后,拿到标准化后的个数为1的单词,怎么找到原单词呢?可以在输入的时候把所有单词记录下来,然后依次把单词标准化后查询value值,如果为1就将原单词插入一个向量中,然后对向量排序即可。
#include<bits/stdc++.h>
using namespace std;
vector<string> vec,vec0;//存放所有原单词和求得单词的向量 
map<string,int>std_word;//标准化后的单词及对应的个数 
string stdstring(string s)//标准化单词函数 
{
	transform(s.begin(),s.end(),s.begin(),::tolower);//转小写 
	sort(s.begin(),s.end());//排序 
	return s;
}
string s;
int main()
{
	int i;
	while(cin>>s)
	{
		if(s=="#")break;
		vec.push_back(s); //将所有原单词插入vec 
		string s0=stdstring(s);//标准化 
		std_word[s0]++;//原来value值为0,出现一次加一次 
	}
	for(i=0;i<vec.size();i++)
	if(std_word[stdstring(vec[i])]==1)vec0.push_back(vec[i]);//只将出现过一次的单词插入所求单词向量 
	sort(vec0.begin(),vec0.end());//所求单词向量排序 
	for(i=0;i<vec0.size();i++)
	cout<<vec0[i]<<endl; 
 } 
  • 栈、队列、优先级队列
  • 集合栈计算机
    在这里插入图片描述
    原题后面几个操作太偏,我只实现前两个操作,主要是思想。这里的集合里面存放的也是集合,集合可以用set来模拟,但程序定义的set数据类型是什么?也是set类型么?这样一来就重复定义,乱套了。因此可以把每一个集合用一个数字来代表,那么程序定义的set就是int型的集合。
  • 团体队列
    在这里插入图片描述
    这题思想很简单啊,设置一个小队列保存人的编号,大队列保存队列编号。用哈希数组来保存每个人属于哪个队列。但去uva做这题耗了我很长时间,因为我犯了一些小错误,写一个比较麻烦的程序的时候犯的小错误终究会酿成大祸。这是小错误吗?这明明是弥天大错。(1)数组开的不够大。RE的最频繁情况就是内存越界,比如数组开的不够大,检查了一下是因为人的编号最大可达999999,而我哈希数组只开到了110000,我以为这够大了,实际上是不够大的。(2)由于这题输入太麻烦,有多个测试实例,因此在循环体内部定义数据可以保证各个测试例子的数据不会干扰。但我习惯与定义全局变量,定义全局变量的时候不需要初始化,而我在主函数结构体内部定义了bool数组居然也没有初始化,导致我调了很久才发现这个bug。你大爷的
#include<bits/stdc++.h>
using namespace std;
int hash1[1100000];
int main()
{
	int n,j=1;
	while(cin>>n)//队列个数 
	{
		if(n==0)break;
		cout<<"Scenario #"<<j<<endl;
		queue<int> q[21000],bigq;//小队列,大队列 
		//记录编号属于哪个队列的哈希数组 
		bool inqueue[21000];//记录哪些编号已经入了大队列,0代表没入,1代表入了 
		int pn,man,i=1;//队列人数,人编号,//队列编号
		memset(inqueue,0,sizeof(inqueue));
		while(n--)
		{
			cin>>pn;//队列人数
			while(pn--)
			{
				cin>>man;
				hash1[man]=i;//编号为man对应的队列编号为i
			 }
			 i++;
		 }
		 string cmd;//指令
		 while(cin>>cmd)
		 {
		 	if(cmd=="STOP")break;
		 	if(cmd=="ENQUEUE")
		 	{
		 		int num;
		 		cin>>num;
		 		q[hash1[num]].push(num); //小队列后面插一个 
		 		if(inqueue[hash1[num]]==0)bigq.push(hash1[num]);//无队友排队,在大队列里面插 
		 		inqueue[hash1[num]]=1;
			 }
			if(cmd=="DEQUEUE")
			{
				int pnum=bigq.front();//大队列中的首队列编号 
				cout<<q[pnum].front()<<endl;
				q[pnum].pop();
				if(q[pnum].size()==0)
				{
					bigq.pop();
					inqueue[pnum]=0; 
				}
			}
		  } 
		j++;
		cout<<endl;
	}
}
  • 优先级队列定义方式为priority_queue< int >pq。默认的这样的构造方法是数越小优先级越小。由于出队列并不一定是最先进去的出队列,所以取队首的操作从q.frront()变成了pq.top()。如果想让数越小优先级越大,可以采用priority_queue< int >,vector< int >,greater < int> >。注意中间要加一个vector< int >,不知道为啥。如果想自定义排序方法如个位数大优先级小或者存结构体,需要用一个rule结构体
struct rule
{
   bool operator()(const int &a,const int &b)//a的优先级比b小返回true
   {
      return a%10>b%10}
}
  • 丑数
    在这里插入图片描述
#include<bits/stdc++.h>
using namespace std;
priority_queue<long long int,vector<long long int>,greater<long long int> >pq;
map<long long int,bool> mp;
int main()
{
	int i=0;
	long long int x;
	pq.push(1);
	mp[1]=1;
	while(1)
	{
		x=pq.top();
		pq.pop();
		i++;
		if(i==1500)break;
		if(mp[2*x]==0)
		{
			pq.push(2*x);
			mp[2*x]=1;
		}
		if(mp[3*x]==0)
		{
			pq.push(3*x);
			mp[3*x]=1;
		}
		if(mp[5*x]==0)
		{
			pq.push(5*x);
			mp[5*x]=1;
		}
	}
	cout<<x;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值