2021-08-06

近日小结

学习了 多源BFS(打卡一道),双段队列广搜(打卡一道),最小步二维转一维模型(打卡一道) 双向广搜(打卡一道)A*(打卡两道)
下面是CF1500的题打卡三道

1. cf1366C

来源:cf1366C
原题链接

请添加图片描述

题目翻译

您将得到一个包含n行(编号从1到n)和m列(编号从1到m)的矩阵。在属于第i行和第j列的单元格中写入数字ai,j,每个数字为0或1。
芯片最初在单元(1,1)中,它将被移动到单元(n,m)。在每次移动过程中,它要么移动到当前行或当前列中的下一个单元格(如果当前单元格是(x,y),则移动后可以是(x+1,y)或(x,y+1))。芯片不能离开矩阵。
考虑芯片从(1,1)到(n,m)的每个路径。如果第一个单元格中的数字等于最后一个单元格中的数字,第二个单元格中的数字等于第二个至最后一个单元格中的数字,则路径称为回文路径,依此类推。
您的目标是更改最小单元格数的值,以便每个路径都是回文的。

输入
第一行包含一个整数t(1≤T≤200)-测试用例的数量。
每个测试用例的第一行包含两个整数n和m(2≤n、 m≤30)-矩阵的尺寸。
接着是n行,第i行包含m个整数ai,1,ai,2,…,ai,m(0≤哎,j≤1).

输出
对于每个测试用例,打印一个整数-您必须更改的最小单元格数,以便矩阵中的每个路径都是回文的

题目大意

给一个n*m的矩阵由0和1构成,从左上角走到右下角每一条路径中第一步等于最后一步第二步等于倒数第二步依次类推,问可以将矩阵中0变1或1变0同时消耗一点能量求消耗能量最少。

解题思路 BFS+贪心 o(n*m)

用多个起始态bfs先将(1,1)和(n,m)入队去更新所有矩阵中的点到最近起点的距离,用map存下每步的总个数和是1的个数,这里会用d数组来维护每个点的距离,最后加上所有点中距离相同点是1的个数和是0的个数取最小值。

已ac代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <unordered_map>
#include <queue>
#include <cstring>
using namespace std;

#define x first
#define y second

typedef long long ll;
typedef pair <int, int> PII;
const int N = 40;

int t, n, m, g[N][N];
int d[N][N];
queue <PII> q;
unordered_map <int, PII> mp;
//用map存下每一步的总个数和是1的个数

int dx[] = {1, -1, 0, 0};
int dy[] = {0, 0, 1, -1};

int bfs(){
	memset(d, -1, sizeof d);
	mp.clear();

	int res = 0;
	q.push({1, 1});//将入口和出口分别入队
	q.push({n, m});
	
	if(g[1][1] != g[n][m]) res++;
	d[1][1] = d[n][m] = 0;

	while(q.size()){
		auto t = q.front();
		q.pop();

		for(int i = 0; i < 4; i++){
			int tx = t.x + dx[i], ty = t.y + dy[i];

			if(tx < 1 || ty < 1 || tx > n || ty > m)continue;
			if(d[tx][ty] != -1)continue;

			int &di = d[tx][ty];
			di = d[t.x][t.y] + 1;
			
			if(mp.count(di) == 0)mp[di] = {1, g[tx][ty]};
			else mp[di] = {mp[di].x + 1, mp[di].y + g[tx][ty]};
			
			q.push({tx, ty});
		}
	}

	for(int i = 1; i <= (n + m - 3) / 2; i++)//矩阵中间那一步不要加
		res += min(mp[i].y, mp[i].x - mp[i].y);
	return res;
}

int main ()
{
	cin.tie(0);
	ios::sync_with_stdio(false);

	cin >> t;
	while(t--){
		cin >> n >> m;
		for(int i = 1; i <= n; i++)
			for(int j = 1; j <= m; j++)
				cin >> g[i][j];
		cout << bfs() << endl;
	}
	return 0;
}

2. cf1263D

题目来源: cf1263D
原题链接
请添加图片描述

题目翻译

一个不知名的黑客想要获得AtForces测试系统的管理员密码,以便在下一次比赛中发现问题。为了实现这一目标,他潜入管理员办公室,偷了一张有n个密码列表的纸——字符串,由拉丁字母组成。黑客回到家里,开始准备攻击AtForces。他发现该系统仅包含被盗列表中的密码,并且该系统确定密码a和密码b的等效性如下:
如果a和b中都存在字母,则两个密码a和b是等效的;如果列表中有密码c,则两个密码a和b是等效的,该密码c与a和b都等效。如果在系统中设置了密码,并应用了等效密码来访问系统,则用户可以访问系统。
例如,如果列表包含密码“a”、“b”、“ab”、“d”,则密码“a”、“b”、“ab”彼此等效,但密码“d”不等效于列表中的任何其他密码。换句话说,如果:管理员的密码是“b”,然后您可以使用以下任一密码访问系统:“a”、“b”、“ab”;管理员的密码是“d”,然后您可以只使用“d”访问系统。
列表中只有一个密码是测试系统中管理员的密码。帮助黑客计算保证访问系统所需的最小密码数。请记住,黑客不知道系统中设置了哪个密码。

输入
第一行包含整数n(1≤N≤2.⋅105)-列表中的密码数。接下来的n行包含列表中的密码–非空字符串si,长度最多为50个字母。某些密码可能相等。
保证所有密码的总长度不超过106个字母。它们都只包含小写拉丁字母。

输出
在一行中打印最小数量的密码,使用该密码可以保证访问系统

题目大意

给你n个由小写字母组成的字符串,只要两个字符串之间有相同的字母即可相互破译只花费一个时间。求最少花费总时间。

解题思路 并查集 o(n*l)

定义二十六个小写字母的并查集,将一个串中的所有字母加入一个集合中,出现过的字母用st记录,然后遍历一遍并查集只要该字母的祖宗结点还指向自己并且在上面所有串中出现过就加一,得到的结果就是答案。

已ac代码

#include <iostream>
#include <algorithm>
#include <string>
using namespace std;

const int N = 30;
int n, p[N];
bool st[N];

int find(int x){
	if(p[x] != x)p[x] = find(p[x]);
	return p[x];
}

int main ()
{
	cin.tie(0);
	ios::sync_with_stdio(false);

	for(int i = 0; i <= 26; i++)p[i] = i;

	cin >> n;
	while(n--){
		string s;
		cin >> s;
		st[s[0]-'a'] = true;
		for(int i = 1; i < s.length(); i++){
			int a = s[i-1] - 'a', b = s[i] - 'a';
			st[a] = st[b] = true;

			a = find(a); b = find(b);
			if(a != b) p[a] = b;
		}
	}

	int ans = 0;
	for(int i = 0; i < 26; i++)
		if(p[i] == i && st[i])ans++;

	cout << ans << endl;
	return 0;
}

3. cf1161A

题目来源:cf1161A
原题链接
请添加图片描述

题目翻译

爱丽丝和鲍勃在一条有n个单元格的线上玩游戏。有n个细胞标记为从1到n。对于每个i,从1到n−1,单元格i和i+1相邻。Alice最初在线路上的某个单元格上有一个标记,Bob试图猜测它在哪里。Bob按顺序猜测线单元号x1、x2、…、xk的序列。在第i个问题中,鲍伯问爱丽丝她的令牌是否在当前席上。也就是说,Alice可以对每个Bob的问题回答“是”或“否”。在这个过程中,最多有一次,在回答问题之前或之后,Alice可以将她的令牌从当前手机移动到相邻的手机。爱丽丝的行为方式使她能够对鲍勃的所有问题回答“不”。请注意,Alice甚至可以在回答第一个问题之前或在回答最后一个问题之后移动她的令牌。爱丽丝也可以选择完全不动。给出了n和Bob的问题x1,…,xk。您想计算让Alice对Bob的所有问题回答“否”的场景数。让(a,b)表示Alice从单元a开始到单元b结束的场景。如果使用ai,则两种场景(ai、bi)和(aj、bj)是不同的ai≠aj或bi≠bj。

输入
第一行包含两个整数n和k(1≤n、 k≤105)-单元格的数量和Bob提出的问题的数量。第二行包含k个整数x1,x2,…,xk(1≤席≤n) -鲍勃的问题。

输出
打印一个整数,即允许Alice对Bob的所有问题回答“否”的场景数。

题目大意

Alice和Bob在玩捉迷藏,有n个格子,Bob会检查k次,第i次检查第xi个格子,如果Alice在这个格子就输了。Alice一开始会在一个格子,可以在Bob检查过一次后或者检查前向一个相邻的格子移动(整局游戏只能用一次)。求Alice有多少种方案赢。

解题思路 思维+枚举 o(n)

枚举起始位置a,如果a未在序列中出现,则对答案有2或3的贡献。若a在序列中出现,找到a第一次出现的位置x,则说明在x位置之前需要把a移动到a+1或a−1处,只需要判断a-1,a+1最后出现的位置是否在x之前。对每种数字记录第一次和最后一次出现位置即可。

已ac代码

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;

const int N = 1e5+10;
int n, k;
bool st[N];
int l[N], r[N];
//分别记录第一次和最后一次出现的位置 

int main ()
{
	cin.tie(0);
	ios::sync_with_stdio(false);
	
	//memset(r, 0x3f, sizeof r);
	
	cin >> n >> k;
	
	int res = 0, x;
	for(int i = 1; i <= k; i++){
		cin >> x;
		if(!st[x]) l[x] = i;
		st[x] = true;
		r[x] = i;
	}
	
	int ans = 0;
	for(int i = 1; i <= n; i++){
		if(!st[i]){//没出现过就有 2 或 3的贡献 
			if(i == 1 || i == n)ans += 2; //边界最多两次贡献 
			else ans += 3;
		}
		else{//出现过就有 0 或 1 或 2的贡献
			if(l[i] > r[i-1] && i > 1) ans++;
			if(l[i] > r[i+1] && i < n) ans++;
		}//相邻格的最后一次比i第一次小就能跳过去 
	}
	
	cout << ans << endl; 
	return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值