- 排序与二分查找
二分查找的一个误区。。。以前一直忽视,直到有天我在这本书上看到了,但还是不以为意。然鹅后果来了,一道题调了好久
像这个代码,其实只能查找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;
}