蓝桥杯三天急救计划 day1(2)

P8686 [蓝桥杯 2019 省 A] 修改数组 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题意:如果一个数之前出现过,就给这个数加1;通过这种方式来实现让数组中没有重复整数

思路:考虑使用并查集,初始化并查集为A数组的数据范围

按题意,输出当前数的父节点,并把当前数的父节点更新为当前点的父节点 + 1即可(按照题目意思的修改方式)

#include<bits/stdc++.h>
#define int long long
 
using namespace std;
typedef pair<int, int> PII;

const int N = 1e6 + 20;

int n;
int p[N]; 

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

void solve()
{
	cin >> n;
	
	for(int i = 1; i <= 1e6 + 10; i ++ ) p[i] = i;
	
	for(int i = 1; i <= n; i ++ )
	{
		int x;
		cin >> x;
		x = find(x);
		p[x] = find(x) + 1; //这个数用过了,下次再输入重复的数就让它的父节点加一就可以了
		cout << x << " "; 
	}
}
 
signed main()
{
	int t = 1;
	//cin >> t;
	while(t -- ) solve();
	return 0;
}

P8630 [蓝桥杯 2015 国 B] 密文搜索 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题意:说白了就是问每一份顺序不定的密码在原文中会匹配多少次

思路:既然顺序不定,那其实更好处理了,匹配的时候直接把密码和部分原串直接按字典序都从小到大排序暴力去匹配就好了

#include<bits/stdc++.h>
#define int long long 

using namespace std;

const int N = 1e3 + 10, mod = 1e9 + 7;

void solve()
{	
	string s;
	cin >> s;
	
	int n;
	cin >> n;
	
	map<string, int> p;
	for(int i = 1; i <= n; i ++ )
	{
		string str;
		cin >> str;
		sort(str.begin(), str.end()); //对所有子串排序 
		p[str] ++;	//用map记录这种情况的子串出现了几次 
	}
	
	int len = s.size();
	int ans = 0;
	for(int i = 0; i < len - 7; i ++ ) //以左端点为标准枚举所有长度为8的子串 
	{
		string tmp = "";
		for(int j = i; j <= i + 7; j ++ ) 
			tmp += s[j];
		sort(tmp.begin(), tmp.end());
		if(p[tmp]) ans += p[tmp]; 
	}
	
	cout << ans << endl;
}

signed main()
{
	int t = 1;
	while(t -- ) solve();
	return 0;
}

P8625 [蓝桥杯 2015 省 B] 生命之树 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题意:就是找出树中节点权值和最大的连通分量(那么输入里面肯定有负数了)

思路:1.首先节点集合可以为空集,所以最后的答案一定是和0取一个max

2.考虑算每个节点下的子树权值和(和0取一个max,形象的理解就是如果某个子树的值成负数了,就把这棵子树割掉了,还是算连通分量),对树来说每个节点都枚举到就相当于所有连通分量就枚举到了。树形dp。

dp[u]  += max(dp[nx], 0ll); 

ps.一般树都是建双向图,然后在dfs通过fa来控制搜的时候不向父节点搜

#include<bits/stdc++.h>
#define int long long
 
using namespace std;
typedef pair<int, int> PII;

const int N = 1e5 + 10;

int n;
int s[N];
vector<int> g[N];
int d[N]; //以每个节点为顶点的连通分量的最大值 

void dfs(int u, int fa)
{
	d[u] = s[u];
	for(int i = 0; i < g[u].size(); i ++ )
	{
		int nx = g[u][i];
		if(nx == fa) continue;
		dfs(nx, u);
		d[u] += max(d[nx], 0ll); //小于0的话这一支就不要了 
	}
}

void solve()
{
	cin >> n;
	for(int i = 1; i <= n; i ++ ) cin >> s[i];
	
	for(int i = 1; i <= n - 1; i ++ )
	{
		int u, v;
		cin >> u >> v;
		g[u].push_back(v);
		g[v].push_back(u);
	}
	
	dfs(1, -1); //fa控制只往下搜 
	
	cout << max(0ll, *max_element(d + 1, d + n + 1)) << endl; 
}
 
signed main()
{
	int t = 1;
	//cin >> t;
	while(t -- ) solve();
	return 0;
}

P8638 [蓝桥杯 2016 省 A] 密码脱落 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题意:至少脱落多少个种子变成当前样子 等价于 问补上多少个字符才能使当前字符串变为回文串

思路:将该字符串逆序,让当前子串与其逆序子串求一个最长公共子序列,n为当前字符串长度,len为这两个串最长公共子序列的长度,n - len即是要补上的长度

#include<bits/stdc++.h>
#define int long long

using namespace std;

const int N = 1e3 +10;
int a[N], b[N];

int f[N][N]; //f[i][j]表示a的前i个字母,和b的前j个字母构成的最长公共子序列长度

void solve()
{
	string s;
	cin >> s;
	int n = s.size();
	s = "$" + s + "$"; //下标映射到从1 - n开始更好做(跑板子) 
	
	string tmp = s;
	reverse(s.begin(), s.end());
	
	for(int i = 1; i <= n; i ++ )
		for(int j = 1; j <= n; j ++ )
		{
			f[i][j] = max(f[i - 1][j], f[i][j - 1]);
			if(s[i] == tmp[j]) f[i][j] = max(f[i][j], f[i - 1][j - 1] + 1); 
		}
	
	cout << n - f[n][n] << endl;
}

signed main()
{
	int t = 1;
	while(t -- ) solve();
	return 0;
}

P8674 [蓝桥杯 2018 国 B] 调手表 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题意:有两个按钮,一个按钮每按一次时间 + 1, 另一个按钮每按一次时间 + k,问以最优策略按键,任两个时间点(从一个时间点调到另一个时间点)间最少需要按多少次按钮才能调过去,然后在这些最小次数中取一个max

思路:1.首先可以想到,算任两个时间点(t1, t2)之间调的次数,等价于算从0时间点开始,调到(t2 - t1)的次数,因为影响按键次数的使两个时间点间的距离

2.既然是问最小多少步,可以考虑转化为图论问题,用bfs解决,队列中存的时间点,每一轮bfs的思路是每一层尝试(尝试的含义是若已经被扩展过就跳过)去扩展两次(一次 + 1, 一次 + k),被扩展的时间点的步数就等于当前点的步数 + 1

#include<bits/stdc++.h>
#define int long long 

using namespace std;

const int N = 1e5 + 10;

int ans;
int dist[N]; //存到达某个时间点所需要按按钮的次数 
bool st[N]; //用来记录到某个时间点的次数是否已经确定(由bfs性质可知,第一次到达的是最优的) 

void solve()
{
	int n, k;
	cin >> n >> k;
	
	queue<int> q;
	q.push(0);
	st[0] = 1;
	
	while(q.size()) 
	{
		int t = q.front();
		q.pop();
		
		ans = max(ans, dist[t]); //最小的这些时间点中最大的一个 
		int to1 = (t + 1) % n, to2 = (t + k) % n;
		if(!st[to1]) //还没有被扩展过 
		{
			dist[to1] = dist[t] + 1; //扩展 
			st[to1] = 1;
			q.push(to1);
		}
		
		if(!st[to2])
		{
			dist[to2] = dist[t] + 1;
			st[to2] = 1;
			q.push(to2);
		}
	}
	
	cout << ans << endl;
}

signed main()
{
	int t = 1;
	while(t -- ) solve();
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值