AtCoder Beginner Contest 391 题解ABCDE

A - Lucky Direction:

题意:给你一个字符串 D 代表八个方向(北、东、西、南、东北、西北、东南、西南)之一。打印与 DD 表示的方向相反的字符串

思路:模拟

    string s;
	cin >> s;
	for(auto op : s){
		if(op == 'W')cout << 'E';
		if(op == 'E')cout <<'W';
		if(op == 'S')cout <<'N';
		if(op == 'N')cout <<'S';
	}

B - Seek Grid

题意:给你一个 N×N 网格 S 和一个 M×M 网格 T 。位于从上往下第 i 行和从左往上第 j 列的单元格用 (i,j) 表示,求一个点x , y,使得S中含x , y的右下方 m * m 个元素与T匹配

思路:M,N<50 。On^4模拟即可


	int n , m;
	cin >> n >> m;
	for(int i = 1;i<=n;i++){
		for(int j = 1;j<=n;j++){
			cin >> s[i][j];
		}
	}
	for(int i = 1;i<=m;i++){
		for(int j = 1;j<=m;j++){
			cin >> t[i][j];
		}
	}
	for(int i = 1;i<=n;i++){
		for(int j = 1;j<=n;j++){
			bool f = true;
			for(int now_i = 0;now_i<m;now_i++){
				for(int now_j = 0;now_j < m;now_j++){
					if(s[i+now_i][j+now_j] != t[now_i+1][now_j+1]){
						f = false;
					}
				}
			}
			if(f == true){
				cout << i << " " << j << endl;
				return 0;
			}
		}
	}

C - Pigeonhole Query

题意:

有 N 只鸽子,编号从 1 到 N ,有 N 个鸽巢,编号从 1 到 N 。最初,鸽子 i 在 1≤i≤N 的巢 i 中。

您会收到 Q 个查询,您必须按顺序处理这些查询。查询有两种类型,每种都以下列格式之一给出:

  • 1 P H : 将鸽子 P 移至鸽巢 H 。
  • 2 : 输出包含一只以上鸽子的鸽巢数量。

思路:比较坑的一点是:把鸽子P移动到鸽巢H,不是把鸽巢P中的鸽子,所以要存储一下鸽子所在的巢穴pos,然后模拟即可


	int n , q;
	cin >> n >> q;
	vector<int> pos(n+1);
	map<int,int>mp;
	for(int i = 1;i<=n;i++){
		pos[i] = i;
		mp[i] = 1;
	}
	int cnt = 0;
	while(q--){
		int t;cin >> t;
		if(t == 1){
			int p , h;
			cin >> p >> h;
			if(mp[pos[p]] == 2)cnt--;
			if(mp[h] == 1)cnt++;
			mp[pos[p]]--;
			pos[p] = h;
			mp[h]++;
		}
		else cout<< cnt << endl;
	}

D - Gravity

题意:在平面直角坐标系中有一些方块,下落的方式与俄罗斯方块一致,每秒钟下落一格,输出Q个查询中,在t+0.5是是否存在区块a

思路:我们发现每个区块消去的时间仅收到以下两个因素影响

1:在不在最底下

2:能不能形成一列

很明显使用vector套小根堆即可,小根堆套一个PII来存储具体的下落时间和位置

struct HeapCmp{bool operator()(const PII &a,const PII &b)const {return a.fi > b.fi;}};
template <typename T> class Heap : public priority_queue<T, vector<T>, HeapCmp> {public:using priority_queue<T, vector<T>, HeapCmp>::priority_queue;};

// Code Start Here	
	int n , w;
	cin >> n >> w;
	vector<Heap<PII>> q(w+1);
	vector<int> ans(n+1,INF);
	for(int i = 1;i<=n;i++){
		int x , y;
		cin >> x >> y;
		q[x].push(mkp(y,i));
	}
	bool f = true;
	while(f){
		int max_val = -INF;
		for(int i = 1;i<=w;i++){
			if(q[i].empty()){
				f = false;
				break;
			}
			max_val = max(max_val,q[i].top().fi);
		}
		if(!f)break;
		else{
			for(int i = 1;i<=w;i++){
				ans[q[i].top().se] = max_val;
				q[i].pop();
			}
		}
	}
	int t;
	cin >> t;
	while(t--){
		int x , y;
		cin >> x >> y;
		if(x >= ans[y])cout <<"No\n";
		else cout <<"Yes\n";
	}
	return 0;	

E - Hierarchical Majority Vote

题意:

对于一个长度为 3^n 的 01 字符串 B=B_1​,B_2,​…,B_3n​,定义一种操作获得长度为 3^n−1 的 01 字符串 C=C_1,​C_2​,…,C_3n−1​:

对于 i=1,2,…,3n−1,令 C_i​ 为 B_3i​、B_3i−1​、B_3i−2​ 中出现次数最多的字符。

现给定一个长度为 3^N 的 01 字符串 A=A_1,​A_2,​…,A_3^N​。设 A′=A_1′​ 是通过 N 次上述操作后得到的长度为 1 的字符串。

请求出最少改变 A 中多少个元素(0 变 1,1 变 0),以改变 A_1′​ 

思路:题意很绕,我们通过几组样例来看是怎么变换的

对于:010011101 

第一层的三个子串 010    011    101

第一层三个子串出现最大值:  0  1  1,组成第二层子串 011

第二层子串  011  只有一个子串,出现最大值  1  A  = 1

在此层修改一个1 为0即可改变A变为1

我们可以采取这样一种办法:对于每个节点,维护要修改的最小代价。

三个节点的值有以下三种情况:

1:三个结点中 0 的个数为 3,a1​=0,将三个儿子中两个 0 都修改,于是 a2​ 的值即为三个儿子中前两小的和。

2:三个结点中 1 的个数为 3, a1​=1,将三个儿子中两个 1 都修改,于是 a2​ 的值即为三个儿子中前两小的和。

3:三个结点中 0 的个数为 2,a1​=0,将三个儿子中的其中一个 0 更改,于是 a2​ 的值为对应的信息 1 为 0 的较小者。

4:三个结点中 1 的个数为 2, a1​=1,将三个儿子中的其中一个 1 更改,于是 a2​ 的值即为对应的信息 1 为 1 的较小者。

综上所述,对于每一层递归,首先将区间划分为三部分,对每部分分别进行递归处理。对于每一层的投票:统计三个子区间的结果,如果所有结果都是 01,则返回该值,并计算出所需的修改次数。如果是两种不同的结果,选择一个值并计算最小的修改次数。

显然这是一个dfs

    int a[N], n, f[N];  // a 存储输入的二进制串,f 用来存储递归的结果

    int t;
	cin >> t;
	n = pow(3, t);
	for (int i=1;i<=n;i++) scanf("%1lld",a+i);
	auto dfs = [&](auto dfs,int l, int r)->PII{
		if (l == r) return {a[l], 1};  // 没有更改
		
		
		PII x = dfs(dfs,l, l + (r - l + 1) / 3 - 1);
		PII y = dfs(dfs,l + (r - l + 1) / 3, l + (r - l + 1) / 3 * 2 - 1);
		PII z = dfs(dfs,l + (r - l + 1) / 3 * 2, r);
		
		int cnt[2] = {0, 0};
		
		cnt[x.first]++;  
		cnt[y.first]++;  
		cnt[z.first]++; 
		
		if (cnt[0] == 3) {
			// 三个部分的投票结果都是 0
			int b[] = {x.second, y.second, z.second}; 
			sort(b, b + 3);
			return {0, b[0] + b[1]};  // 返回最小的两个
		}
		else if (cnt[1] == 3) {
			// 三个部分的投票结果都是 1
			int b[] = {x.second, y.second, z.second};
			sort(b, b + 3); 
			return {1, b[0] + b[1]};  // 返回最小的两个
		}
		else if (cnt[0] == 2) {
			// 如果有两个部分的投票结果是 0,则返回投票结果为 0,并取修改次数最少的部分
			int ans = 1e18;
			if (x.first == 0) ans = min(ans, x.second);  // 若当前部分的投票结果是 0,取其修改次数
			if (y.first == 0) ans = min(ans, y.second); 
			if (z.first == 0) ans = min(ans, z.second);  
			return {0, ans};
		}
		else if (cnt[1] == 2) {
			// 如果有两个部分的投票结果是 1,则返回投票结果为 1,并取修改次数最少的部分
			int ans = 1e18;
			if (x.first == 1) ans = min(ans, x.second); 
			if (y.first == 1) ans = min(ans, y.second);  
			if (z.first == 1) ans = min(ans, z.second); 
			return {1, ans};
		}
	};
	cout <<dfs(dfs,1,n).second;
	return 0;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值