AtCoder Beginner Contest 213 题解

AtCoder Beginner Contest 213

A Bitwise Exclusive Or

题意:给定0<=A,B<=255,找到一个非负整数C,满足A XOR C=B
XOR为异或运算,因为A,B都很小直接暴力模拟就行了。

#include<stdio.h>
int main()
{
	int a,c,i;
	scanf("%d%d",&a,&c);
	for(i=0;i<=255;i++)
	{
		if((a^i)==c){
			printf("%d",i);
			break;
		}
	}
	return 0;
}

不过注意异或运算符^的优先级极低,判断时要记得加括号if((a^i)==c)

B - Booby Prize

题意:给出长度为n的一串数字,求倒数第二大的数字是第几个出现的
列如n=6 1 123 12345 12 1234 123456第二大的数字是12345,是第3个出现的于是答案是3.
我们只需要用结构体存值以及出现的时的编号,sort排序后输出其id即可

#include<stdio.h>
#include<algorithm>
using namespace std;
const int N=2e5+3;
int a[N];
struct node
{
	int a,id;
}s[N];
bool cmp(node a,node b)//重载运算
{
	return a.a>b.a;
}
int main()
{
	int n,i;
	scanf("%d",&n);
	for(i=0;i<n;i++){
		scanf("%d",&s[i].a);
		s[i].id=i+1;
	}
	sort(s,s+n,cmp);
	printf("%d",s[1].id);
	return 0;
}

C - Reorder Cards

题意 对数字1~n给出其所在的位置(行列)然后将没有数字的行列删去,问删去后的这些数字所在的行列是多少。H,W代表行列的大小1≤H,W≤10^9

我们可以用结构体数组row[N],col[N]分别记录每个数字所在的行列和数字本身,使其值为1,然后再按行列的编号按升序排序计算前缀和,分别输出数字的行列就行了。
举个例子

4 5 2
3 2//数字1在第3行第2列
2 5//数字2在第2行第5列
原来的图45列的矩阵:
*****
****2
*1***
*****
那么row记录的是 
	     数字:0,2,1,0,0//
           值:0,1,1,0,0//因为数字1,2分别出现在2,3行,所以下标2,3的值就是1,其他下标的值是0
	   前缀和:0,1,2,2,2
下标代表行编号:0,1,2,3,4
     

计算前缀和和后数字1的行号就是2,数字2的行号就是1
同理列也可以这样计算
最终答案就是

2 1
1 2
删去行列后的图:
*2
1*

但我们注意到N H的范围如果还用下标代表行列编号的话无疑会爆内存于是我们可以用map进行离散化,再按上述思路求解

#include<map>
#include<stdio.h>
using namespace std;
const int N=1e5+3;
//用map代替结构体
map<int,int>row;//行数组
map<int,int>col;//列数组
struct node
{
	int r,c;
}s[N];
int main()
{
	int h,w,n,r,c,k,i;
	map<int ,int >::iterator inde,j;//定义迭代器
	scanf("%d%d%d",&h,&w,&n);
	for(i=1;i<=n;i++)
	{
		scanf("%d%d",&r,&c);
		row[r]=1;
		col[c]=1;
		s[i].r=r,s[i].c=c;
	}
	for(inde=row.begin(),k=0;inde!=row.end();k++,inde++)
	{
		if(k>0) inde->second+=j->second;
		j=inde;
	}
	for(inde=col.begin(),k=0;inde!=col.end();k++,inde++)
	{
		if(k>0) inde->second+=j->second;
		j=inde;
	}
	for(i=1;i<=n;i++)
		printf("%d %d\n",row[s[i].r],col[s[i].c]);
	return 0;
}

D - Takahashi Tour

题意:城市1~n,从1出发只走未访问过的相连的城市中编号最小的,当没有城市可以走时允许沿着原路返回。当所有城市都访问完后输出访问的顺序。
用vector存图并排序(因为要求访问编号小的)跑一遍dfs就行了,边跑边输出

#include<vector>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int N=2e5+3;
bool vis[N];
vector<int>g[N];
void dfs(int u)
{
	printf("%d ",u);//第一次走到输出一次
	vis[u]=1;
	for(int i=0;i<g[u].size();i++)
	{
		int v=g[u][i];
		if(!vis[v]){
			vis[v]=1;
			dfs(v);
			printf("%d ",u);//原路返回时再输出一次
		}
	}
}
int main()
{
	int n,i,u,v;
	scanf("%d",&n);
	for(i=0;i<n-1;i++)
	{
		scanf("%d%d",&u,&v);
		g[u].push_back(v);
		g[v].push_back(u);
	}
	for(i=1;i<=n;i++)
		sort(g[i].begin(),g[i].end());//对每个城市能走到的城市按编号大小排序
	dfs(1);
	return 0;
}

E - Stronger Takahashi

题意:n*m的地图,‘.’代表能街道,‘#’代表障碍。你可以消耗一格体力摧毁一个2x2的方格内所有障碍,问从起点(1,1)到终点(n,m)最少需要消耗多少体力。
这道题在比赛中并没能想出解法,想到了bfs的解法但困扰我的地方在如何记录我摧毁的地方,防止重复遍历。在赛后看了别人的代码后才明白只需要将所有可能摧毁的地方都入队即可。
在bfs中如果下一步(x,y)不是障碍直接将其入队即可,且不消耗体力,如果下一步是障碍那么我们会有四个可能的摧毁方向,向左上,右上,左下,右下摧毁,因为会有重叠,这四个2x2的方格叠在一起刚好是一个以(x,y)为中心的九宫格,将这九个位置全部入队且体力花费+1就可以了。
本题可以使用优先队列维护最小的体力耗费,也可以用双端队列,花费体力的插入队尾,不花费体力的插入队首,每次取都取队首元素。

#include<queue> 
#include<stdio.h>
using namespace std;
const int N=503;
int n,m;
char a[N][N];
int dx[]={1,0,-1,0};
int dy[]={0,1,0,-1};
bool vis[N][N];
struct node
{
	int r,c,step;
};
bool check(int x,int y)
{
	if(x<1||y<1||x>n||y>m||vis[x][y])return 0;
	return 1;
}
void bfs()
{
	deque<node>qu;//双端队列 
	qu.push_front({1,1,0});//在队首入队
	while(!qu.empty())
	{
		node u=qu.front();//取出队首
		qu.pop_front();
		if(vis[u.r][u.c])continue;
		vis[u.r][u.c]=1;
		if(u.r==n&&u.c==m){
			printf("%d",u.step);
			return ;
		} 
		for(int i=0;i<4;i++)
		{
			int x=u.r+dx[i], y=u.c+dy[i];
			if(!check(x,y))continue;
			if(a[x][y]=='.')//下一步不是障碍
			{
				qu.push_front({x,y,u.step});//花费不增加,将其放入队首 
			}
			else
			{
				for(int j=x-1;j<=x+1;j++)//遍历所有可能的摧毁2*2的墙后不需要花费就能到的点 
				{
					for(int k=y-1;k<=y+1;k++)
					{
						if(check(j,k)){
							qu.push_back({j,k,u.step+1});//花费加一,在队尾入队 
						}
					}
				}
			}
		} 
	} 
}
int main()
{
	int i,j;
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;i++)
	{
		scanf("%s",a[i]+1);
	}
	bfs();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值