AtCoder 235 A~E题

AtCoder Beginner Contest 235题解



A - Rotate

【题目链接】A - 旋转 (atcoder.jp)

【代码实现】

#include<iostream>
#include<algorithm>
#include<cstring>
#include<sstream>

using namespace std;

//abc+bca+cab: 
//123 231 312 :231在123基础上将1移到23后 312在231的基础上将2移到最后面 
//也就说:从第二个数开始,每一个数都是在前一个的基础上将第一个字符移到最右边
//因此我们可以先用字符串构造这3个数,在将它们转为数字即可! 

int main()
{
	string s1, s2, s3;
	cin >> s1;
	char a = s1[0], b = s1[1];
	 
	 s2 = s1.substr(1,3) + a;
	 s3 = s2.substr(1,3) + b;
	 
//	 cout << s1 << ' ' << s2 << " " << s3 << endl;
	 int num1 = stoi(s1), num2 = stoi(s2), num3 = stoi(s3);
	 cout << num1 + num2 + num3 ;

	 
	return 0;
}

B - Climbing Takahashi

【题目链接】B - 攀登高桥 (atcoder.jp)

【代码实现】

#include<iostream>
#include<algorithm>
#include<cstring>
#include<sstream>

using namespace std;

typedef long long LL; 
const int N = 1e5 + 10;
int a[N];

// 找到 第一个 >= 它右边第一个数的数 

int main()
{
	
	int n;
	scanf("%d", &n);
	for(int i = 0; i < n ; i ++) scanf("%d", &a[i]);
	
	for(int i = 0; i < n; i ++)
	{
		if(a[i] >= a[i + 1])
		{
			printf("%d", a[i]);
			break;
		}
	}
	
	return 0;
}

C - The Kth Time Query

【题目链接】C - 第 K 次时间查询 (atcoder.jp)

刚刚开始想到的就是二分,然后给搜到的数做个标记,跳过出现过的位置,balabala搞不出来,后面看了题解,可以用一个哈希表将这个数和该数出现的所有位置做一个映射!

【代码实现】

#include<iostream>
#include<algorithm>
#include<cstring>
#include<unordered_map>

using namespace std;

//关联数组:将输入的数a 与 a出现的所有位置关联起来 ——映射关系(哈希表里边嵌套使用vector)  
//序列 1 1 2 3 2 1
//如 m[1]={1, 2, 6}
//   m[2] = {3,5}
//   m[3] = {4} 

int main()
{
	int n , q;
	scanf("%d%d", &n, &q);
	
	unordered_map<int, vector<int>> query;
	
	for(int i = 1; i <= n; i ++)
	{
		int a;
		scanf("%d", &a);
		query[a].push_back(i);
		
	}
	while(q --)
	{
		int a, k;
		scanf("%d%d", &a, &k);
		
		if(query.find(a) == query.end()) puts("-1");// 没有这个数
		else if(query[a].size() < k) puts("-1");//查询次数 超过 该数的出现次数
		else printf("%d\n", query[a][k - 1]); // 注:vector里边存的数下标从0开始 
		
	}
	return 0;
}

D - Multiply and Rotate

【题目链接】D - 乘法和旋转 (atcoder.jp)

题目意思:

给定初始值为 x = 1, 两种操作方式

  • 第一种是将 x 替换为 a * x,
  • 第二种是将 x 视为字符串,当前仅当满足 x 大于等于 10且x 不能被 10 整除, 将 x 的最右端字符放到最左端。

求解 由x = 1变到N 时的最小操作次数。

思路:

初始状态1,目标状态N

BFS最小步数模型,状态为操作1的相乘和操作2的换位(根据题意转为字符串后将换位拼接即可)

注:

a与N的范围都在2~10^6之间,对于操作1,当 当前状态 * a(相乘)可能会导致结果溢出,为此要直接开long long

【代码实现】

#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#include<unordered_map>

using namespace std;

queue<int> q;
const int N = 1e6 + 10;

typedef long long LL;

int d[N];
bool st[N];

LL a, n; // 为了防止 a 与 N 相乘结果溢出 直接开long long!  坑啊!  

int bfs()
{
	q.push(1);
	d[1] = 0;
	
	while(q.size())
	{
		auto t = q.front();
		q.pop();
		
		if(t == n) return d[n];
		
		if(st[t]) continue;
		st[t] = true;
		
		//扩展队头元素
		if( t * a <= N)
		{
			q.push(t * a);
			d[t * a] = d[t] + 1;	
		}
		if(t > 10 && t % 10 != 0)
		{
			string str = to_string(t);
			int len = str.size();
			char a = str[len - 1];
			string newStr = a + str.substr(0, len - 1);	
			int nums = stoi(newStr);
			q.push(nums);
			d[nums] = d[t] + 1;
		} 
	}
	
	return -1;
	
}

int main()
{
	cin >> a >> n;
	
	cout << bfs();
	
	return 0;
}

E - MST + 1

【题目链接】E - MST + 1 (atcoder.jp)

题意:
现在有一个连通图,且保证最小生成树唯一。你可以有q 次操作,每次操作可以向图中加入一条原本不存在的边,且保证加完此边之后最小生成树仍然唯一,询问你当前加入的这条边是否是最小生成树的树边。
注意:每次操作都是独立的,如:第一次操作完后,并不会真的向图中加入一条边,每次加边操作时的图都是初始图。

题解:
知识点:克鲁斯卡尔算法。此题时克鲁斯卡尔算法转换思路,对于 Q 次查询每次都跑一边 Kruskal 时间复杂度为 O ( qn logn ) ,观察得在使用 Kruskal 建立最小生成树时有很多重复步骤,现可以直接将查询边放入至原图中直接跑 Kruskal ,但并不参与实际建树过程,未在同一连通分量中的点进行判断是否为查询边,若是则加入答案集合,否则直接连边。

【补题】
原文链接:本题题解参考自该博客

【代码实现】

#include<iostream>
#include<algorithm>
#include<vector>
#include<set>

using namespace std;

const int N = 1e5 + 10, M = 2 * N;
int p[M];
int n, m, q;
struct Edge
{
	// 通过pos 和 flag 判断最小生成树是否包含查询边 
	int a, b, w, pos;// pos用于判断是否用到了操作i 
	bool flag; // flag用于区别是否为查询边和原始边,参不参与建树 
	bool operator< (const Edge &W)const
	{
		return w < W.w;
	}
}edge[M];

vector<Edge> e;// 存储边集 
set<int> se;//记录操作:用于判断操作i是否用到了查询边 

int find(int x)
{
	if(p[x] != x) p[x] = find(p[x]);
	return p[x];
}
void kruskal()
{
	sort(e.begin(), e.end());
	
	for(int i = 1; i <= n; i ++) p[i] = i;
	
	for(int i = 0; i < e.size(); i ++)
	{
		int a = e[i].a, b = e[i].b, w = e[i].w;
		a = find(a), b = find(b);
		if(a != b)
		{
			//如果是查询的边 做个记录(要它) 但不参与建树(合并) 
			if(e[i].flag) se.insert(e[i].pos);
			else p[a] = b;// 若不是,查询边 则合并建树 	
		}
	}
}
int main()
{
	cin >> n >> m >> q;
	for(int i = 1; i <= m; i ++)
	{
		int a, b ,w;
		cin >> a >> b >> w;
		edge[i] = {a, b, w, 0, false}; 
		e.push_back(edge[i]);
	} 
	for(int i = 1; i <= q; i ++)
	{
		int a, b ,w;
		cin >> a >> b >> w;
		edge[i] = {a, b, w, i, true};// pos对应查询的编号 
		e.push_back(edge[i]);
	}
		
	kruskal();
	 
	for(int i = 1; i <= q; i ++)
	{
		if(!se.count(i)) puts("No");
		else puts("Yes"); 

	}
	return 0;
	
}
  • 7
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值