队列:一边入队,一边出队
queue、双端队列deque、优先队列priority_queue
优先队列:因为优先队列本质是堆,注意比较函数的写法
struct node{
string name;
int pri;
friend bool operator < (node f1,node f2){
return f1.pri<f2.pri; //友元函数,写在结构体里面的,意思时价格大的优先!!!是反的
}
};
priority_queue<node> q;
struct node{
string name;
int pri;
};
//写在外面
struct cmp{
bool operator () (node a,node b){
return a.pri>b.pri;
}
};
//定义的时候
priority_queue<node,vector<node>, cmp> q; ///greater(),换为了cmp
//如果数据庞大,那么使用引用来提高效率
const node &a
一本通上面的题目
1、周末舞会
一边出队,马上入队
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int l1,r1,l2,r2;
int one[101],two[101];
int n1,n2,m;
int main(){
cin>>n1>>n2>>m;
for(int i=1;i<=n1;i++) one[i]=i;
for(int i=1;i<=n2;i++) two[i]=i;
l1=1;l2=1;r1=n1;r2=n2;
for(int i=1;i<=m;i++){
cout<<one[l1]<<" "<<two[l2]<<endl;
one[++r1]=one[l1];
two[++r2]=two[l2];
l1++;
l2++;
}
return 0;
}
2、Blah数集
两个头指针
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int m,n;
long long stack[1000000];
int main(){
while(cin>>m>>n){
int two=1,three=1,rear=2; //队列需要两个指针,这里有两个头指针
stack[1]=m;
while(rear<=n){
long long t1=stack[two]*2+1;
long long t2=stack[three]*3+1;
long long t=min(t1,t2);
if(t1<t2) two++;
else three++;
if(t==stack[rear-1]) continue;//去重
else stack[rear++]=t;//否则就加入
}
cout<<stack[n]<<endl;;
}
return 0;
}
1334:【例2-3】围圈报数
注意一下now一开始应该是从n开始的,因为代码顺序问题
next[i]=i+1 next[n]=1
#include<iostream>
#include<stdio.h>
#include<cmath>
#include<cstring>
#include<queue>
using namespace std;
#define INF 1e9+10
int next[101];
int main(){
int n,m;
cin>>n>>m;
int out=0,ans=1;
for(int i=1;i<n;i++) next[i]=i+1;
next[n]=1;
int now=n;
while(out<n){
while(ans<m){
now=next[now];
ans++;
}
cout<<next[now]<<" ";
out++;
next[now]=next[next[now]];
ans=1;
}
return 0;
}
类似的一个题:猴子选大王
2<=n<=1000,2<=k<=10^9
输出最后一只猴子,每一次出队的猴子是对剩下的猴子总数remain取余为k的猴子
这里有一个细节就是k需要对n进行取模计算(每一次都需要,因为总数会减少),不然一次次的数下去会超时
#include<iostream>
#include<stdio.h>
#include<cmath>
#include<cstring>
#include<queue>
using namespace std;
#define INF 1e9+10
struct monk{
int id,next;
}a[1010];
int main(){
int n,k,remain;
cin>>n>>k;
for(int i=1;i<n;i++){
a[i].id=i;a[i].next=i+1;
}
a[n].id=n;a[n].next=1;
remain=n;
int kk=k%remain;
if(kk==0) kk=remain; //!!!
int cur=1,pre=n,count=0;
while(remain>1){
count++;
if(count==kk){
a[pre].next=a[cur].next;
remain--;
count=0;
kk=k%remain; //每一次都需要取模
if(kk==0) kk=remain;
}
else pre=cur;
cur=a[cur].next;
}
cout<<a[cur].id<<endl;
return 0;
}
and一个 取牌游戏
k张纸牌,n-1个朋友,n<=k<=100000,k是n的倍数,纸牌中有m=k/n张 good牌和k-m张bad牌,小明想要所有的good牌
游戏开始时候,会发给小明右手边的人,每发完一个人,就把接下来的p张牌往后移,逆时针发牌,需要输出所有的good牌所在位置(升序)
#include<iostream>
#include<stdio.h>
#include<cmath>
#include<string>
#include<queue>
#include<algorithm>
using namespace std;
#define maxn 1500000
//这里需要推算出大概的数据范围:k+10*n*(100000/n)=1100000
int a[maxn],b[maxn];
//a是牌的队列,b是小明拿到牌的队列
int n,k,p;
int main(){
cin>>n>>k>>p;
int front=0,tail=k,q=0;
for(int i=1;i<=k;i++) a[i]=i;
for(int i=1;i<=k/n;i++){
for(int j=1;j<=n;j++){
front++;
if(j==n) b[++q]=a[front];
for(int t=1;t<=p;t++){
front++;
a[++tail]=a[front];
}
}
}
sort(b+1,b+1+q);
for(int i=1;i<=k/n;i++) cout<<b[i]<<endl;
return 0;
}
3、连通块,这个还蛮有用的,手写队列
查找四个方向上有多少个连通块 map[n][n]、vis[n][n]、que[n][2]
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int n,m;
int map[101][101];
int dis[101][101];
int queue[10001][2];
int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
int ans;
void bfs(int x,int y){
int head=1;
int rear=2;//这是队列!!!这两个总的有
queue[1][0]=x;queue[1][1]=y;
while(head<rear){
x=queue[head][0];
y=queue[head][1];
head++;
for(int i=0;i<4;i++){
int x1=x+dir[i][0];
int y1=y+dir[i][1];
if(x1>0&&x1<=n&&y1>0&y1<=m&&map[x1][y1]&&!dis[x1][y1]){
dis[x1][y1]=1;
queue[rear][0]=x1;
queue[rear++][1]=y1;//入队
}
}
}
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++) cin>>map[i][j];
memset(dis,0,sizeof(dis));
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
if(map[i][j]&&!dis[i][j]){
bfs(i,j);
ans++;
}
}
cout<<ans<<endl;
return 0;
}
4、围成面积
这道题计算被1围起来的0的个数,边缘都是0,这道题的想法就是!!把四周的0都变为1,最后枚举剩下的0,就相当于要从正方形的四条边去bfs
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
using namespace std;
//这道题得思路很难想,从边缘开始搜索,标记没有被1包围的0使他们和 1 成为同样的节点,最后枚举剩余的0即可
int map[20][20];
int queue[101][2];
int dis[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
void bfs(int x,int y){
int head=1,tail=0;
map[x][y]=-1;//使这些0变为-1
queue[head][0]=x;
queue[head][1]=y;
while(head!=tail){
tail++;
for(int i=0;i<4;i++){
int nx=queue[tail][0]+dis[i][0];
int ny=queue[tail][1]+dis[i][1];
if(map[nx][ny]==-1) continue;
else map[nx][ny]=-1;
head++;
queue[head][0]=nx;
queue[head][1]=ny;
}
}
}
int main(){
int ans=0;
memset(map,255,sizeof(map)); //首先要先初始化map为-1 !!!!!(255=-1) //能够在搜索的时候退出
//所以map[0][i],map[i][0]等都是-1
for(int i=1;i<=10;i++) //这里从1开始,是可以不用再bfs里面判断其下标合不合理
for(int j=1;j<=10;j++) {
cin>>map[i][j];
map[i][j]=-map[i][j];//注意这里的标记,既可以用来表示其代表的值,也可以用来表示有没有访问过
}//这里0还是0,1变为了-1
for(int i=1;i<=10;i++){
if(!map[i][1]) bfs(i,1);
if(!map[i][10]) bfs(i,10);
if(!map[1][i]) bfs(1,i);
if(!map[10][i]) bfs(10,i);//如果是0就开始搜索
}
for(int i=1;i<=10;i++)
for(int j=1;j<=10;j++) if(map[i][j]==0) ans++;//这个时候还有的0就是被包围的,因为从边缘搜索得到的0全部都被标记为了-1
cout<<ans<<endl;
return 0;
}
1360:奇怪的电梯(lift)
这里使用了一个多的数组sum[n]来保证最少的次数记录
pre[n]、num[n]、vis[n]、sum[n]
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
using namespace std;
int pre[10001],aa[10001],vis[10001];
int sum[10001];
//pre表示当前的楼层(队列),aa表示能上升和下降的数量
//vis表示是否经过(保证最小距离)sum表示所用的次数!!!!(useful)
int n,a,b;
int main(){
cin>>n>>a>>b;
for(int i=1;i<=n;i++) cin>>aa[i];
int head=0,tail=1;
pre[tail]=a;
memset(vis,0,sizeof(vis));
vis[a]=1;//确保只能去一次
do{
head++;
for(int i=-1;i<=1;i+=2){//模拟上下两次选择
if(pre[head]+i*aa[pre[head]]>=1&&pre[head]+i*aa[pre[head]]<=n&&!vis[pre[head]+i*aa[pre[head]]]){
tail++;
pre[tail]=pre[head]+i*aa[pre[head]];
vis[pre[head]+i*aa[pre[head]]]=1;
sum[tail]=sum[head]+1;//这个数列的作用就相当于queue[n][3] //不能写为ans++
}
}
}while(head<tail&&pre[head]!=b);//到达目标在这里判断
if(a==b) cout<<0<<endl;
else if(pre[tail]==b) cout<<sum[tail]<<endl;
else cout<<"-1"<<endl;
return 0;
}
5、产生数,给定一个数n和k个变换规则,求一共有多少种变换结果,注意去重,永恒的vis数组
其实没什么很复杂的,就是一位一位的改数字,如果不是重复的就放进去,不断让每一位都得到改变,尝试所有的可能
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<queue>
#include<cstdio>
using namespace std;
int n,k;
int be[16],af[16];
int vis[10001];
queue<int> qu;
int main(){
cin>>n>>k;
memset(vis,0,sizeof(vis));
for(int i=1;i<=k;i++) cin>>be[i]>>af[i];
qu.push(n);
vis[n]=1;
int sum=1;
while(!qu.empty()){
int x=qu.front(),y=qu.front();
//cout<<qu.front()<<endl;
qu.pop();
int mod=1;
while(x>0){
int temp=x%10;
x/=10;
for(int i=1;i<=k;i++){
if(be[i]==temp){
int yy=y+(af[i]-be[i])*mod;//因为是一位一位处理的
if(!vis[yy]){
sum++;
vis[yy]=1;
qu.push(yy);
}
}
}
mod*=10; //记住这里的操作!斯国一!
}
}
cout<<sum<<endl;
return 0;
}
1362:家庭问题(family)
这个是并查集
#include<iostream>
#include<stdio.h>
#include<cmath>
#include<cstring>
#include<queue>
using namespace std;
#define INF 1e9+10
int fa[201];
bool isroot[201];
int sum[201];
int n,k;
int findf(int x){
int a=x;
while(x!=fa[x]) x=fa[x];
while(a!=fa[a]){
int z=a;
a=fa[a];
fa[z]=x;
}
return x;
}
void uni(int x,int y){
int faa=findf(x);
int fab=findf(y);
if(faa!=fab) fa[faa]=fab;
}
void inti(int n){
for(int i=1;i<=n;i++){
isroot[i]=0;
fa[i]=i;
}
}
int main(){
cin>>n>>k;
inti(n);
int xx,yy;
for(int i=1;i<=k;i++){
cin>>xx>>yy;
uni(xx,yy);
}
for(int i=1;i<=n;i++){
isroot[findf(i)]=1;
sum[findf(i)]++;
}
int ans=0,maxn=-1;
for(int i=1;i<=n;i++){
ans+=isroot[i];
maxn=max(maxn,sum[i]);
}
cout<<ans<<" "<<maxn<<endl;
return 0;
}
posted on
浙公网安备 33010602011771号