CSDN竞赛12期题解

总结

这次竞赛还是存在些许bug,题目难度中等,没有特别难的题目,也没有送分题。被bug和一些题目的数据卡了一段时间,最终通过还是花了不少时间的。

题目列表

1.豚鼠排名榜

题目描述

已知字符A.B,C。每个字符都有自己的权值q。
现不知道权值q,只知道A,B,C的三次比较结果。

输入描述:

输入三个字符串表示三次比较的结果

输出描述:

输出结果,如果不存在输出”Impossible”

输入样例:

A<B

B>C

C>A

输出样例:

ACB

分析

首先,吐槽下这题的自测输入的bug,考试环境输入 < < <会被自动转化为 & l t \&lt &lt,所以你用自测输入来测试程序会一直出错,这点卡了我挺久,检查和修改了若干遍代码后,才发现自测输入有问题。貌似在评论区回复小于号也会自动转化为 & l t \&lt &lt

题目意思给的输入应该是ABC两两比较的结果,三次比较结果矛盾就输出”Impossible”,这里输出结果不存在我理解为是出现 A < B , B < C , C < A A<B,B<C,C<A A<B,B<C,C<A这样类似的情况,字符的偏序关系存在一个环就矛盾,这点很好判断。但是题意只是说给出三次比较的结果,要是输入出现 A < B , B > A , C < B A<B,B>A,C<B A<B,B>A,C<B,这种情况呢?意味着我们知道B最大,却不知道A和C的偏序关系,这种情况是否也表示结果不存在。再或者三次都给出的是A和B的偏序关系,我们是需要输出A和B的偏序关系还是输出”Impossible”,题目都没有告知我们,所以题意没讲清楚就很坑。

考试时我的想法是:既然是T1,肯定是思路越简单越好,题意输出结果不存在的情况应该就是出现矛盾的情况。所以要想完整表示三个字符间的偏序关系,必然会出现一个字符大于其他两个,一个字符大于另一个,那么比字符小的字符个数依次是012,就比如用例,从小到大是ACB,那么A大于0个字符,C大于1个字符,B大于2个字符。我们遍历输入三个表达式的时候统计下每个字符大于其他字符的次数,如果最终三个字符大于其他字符的次数分别是0,1,2,结果就是合理的,否则输出”Impossible”。

代码

#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
using namespace std;
unordered_map<char,int> m;
std::string solution(std::string exp1, std::string exp2, std::string exp3) {
	vector<string> t = {exp1,exp2,exp3};
	m['A'] = m['B'] = m['C'] = 0;
	for(auto x : t) {
		if (x[1] == '>') m[x[0]]++;
		else m[x[2]]++;
	}
	string res = "ABC";
	int cnt[3] = {0};
	for (auto x : m) {
		cnt[x.second]++;
		res[x.second] = x.first;
	}
	for(int i = 0; i < 3; i++) {
		if(cnt[i] != 1) return "Impossible";
	}
	return res;
}
int main() {
	std::string exp1;
	std::string exp2;
	std::string exp3;
	cin>>exp1;
	cin>>exp2;
	cin>>exp3;
	std::string result = solution(exp1, exp2, exp3);
	std::cout<<result<<std::endl;
	return 0;
}

2.字符串转换

题目描述

已知一个字符串a,b。 字符串b中包含数量不等的特殊符号“.”,“*”(字符串存在没有特殊符号或者全由特殊符号组成的情 况)。 “.”表示该字符可以变成任意字符,“* ”表示该字符的前一个字符可以变成任意多个。 现在我们想知道b可否通过特 殊符号变成a。 a* 可以转化为a,aa,aaa,aaaa…

分析

这道题对于会做的同学很简单,可以说能做出来的肯定做过这道题,因为如果连这么经典的题都没做过,那么一般做不出来这道题,对于第一次遇见这道题的同学来说,难度还是不小的。之前写的这道题的题解AcWing 30 正则表达式匹配

这道题与正则表达式匹配还是有点区别的,因为正则表达式匹配中*可以使得前面字符出现0次,处理起来更加复杂,而本题指出了*前面字符至少出现一次,这样处理起来就简单了。

状态表示: f [ i ] [ j ] f[i][j] f[i][j]表示 a a a的前 i i i个字符与 b b b的前 j j j个字符是否匹配, t r u e true true表示匹配, f a l s e false false表示不匹配。

状态边界: f [ 0 ] [ 0 ] = t r u e f[0][0]=true f[0][0]=true表示两个字符串都是空的情况是匹配的。

状态转移方程:

  • b [ j − 1 ]   ! =   ′ ∗ ′ b[j-1] \ !=\ '*' b[j1] != 时,
    f [ i ] [ j ] = { f [ i − 1 ] [ j − 1 ] if  a [ i − 1 ] = = b [ j − 1 ]   o r   b [ j − 1 ] = = ′ . ′ f a l s e , a [ i − 1 ] ! = b [ j − 1 ]   a n d   b [ j − 1 ] ! = ′ . ′ f[i][j]= \begin{cases} f[i-1][j-1]& \text{if\ $a[i-1]==b[j-1]\ or\ b[j-1]=='.'$}\\ \\ false,& \text{$a[i-1]!=b[j-1]\ and\ b[j-1]!='.'$} \end{cases} f[i][j]=f[i1][j1]false,if a[i1]==b[j1] or b[j1]==.a[i1]!=b[j1] and b[j1]!=.

  • b [ j − 1 ]   = =   ′ ∗ ′ b[j-1] \ ==\ '*' b[j1] == 时,

f [ i ] [ j ] = { f a l s e , if  b [ j − 2 ]   ! =   a [ i − 1 ]  and  b [ j − 2 ]   ! =   ′ . ′ f [ i − 1 ] [ j ]   ∣   f [ i ] [ j − 1 ] , if  b [ j − 2 ]   = =   a [ [ i − 1 ]  or  b [ j − 2 ]   = =   ′ . ′ f[i][j]= \begin{cases} false, & \text{if\ $b[j-2]\ !=\ a[i-1]$\ and\ $b[j-2]\ !=\ '.'$}\\ \\ f[i-1][j]\ |\ f[i][j-1],& \text{if\ $b[j-2]\ ==\ a[[i-1]$\ or\ $b[j-2]\ ==\ '.'$} \end{cases} f[i][j]=false,f[i1][j]  f[i][j1],if b[j2] != a[i1] and b[j2] != .if b[j2] == a[[i1] or b[j2] == .

由于状态的初始值是false,所以我们只需要考虑状态可能转移为true的情况。即:
f [ i ] [ j ] = { f [ i − 1 ] [ j − 1 ] if  a [ i − 1 ] = = b [ j − 1 ]   o r   b [ j − 1 ] = ′ . ′ f [ i − 1 ] [ j ]   ∣   f [ i ] [ j − 1 ] , if  b [ j − 1 ]   =   ′ ∗ ′  and ( b [ j − 2 ]   = =   a [ [ i − 1 ]  or  b [ j − 2 ]   = =   ′ . ′ ) f[i][j]= \begin{cases} f[i-1][j-1]& \text{if\ $a[i-1]==b[j-1]\ or\ b[j-1]='.'$}\\ \\ f[i-1][j]\ |\ f[i][j-1],& \text{if\ $b[j-1]\ =\ '*'$\ and\ ($b[j-2]\ ==\ a[[i-1]$\ or\ $b[j-2]\ ==\ '.'$)} \end{cases} f[i][j]=f[i1][j1]f[i1][j]  f[i][j1],if a[i1]==b[j1] or b[j1]=.if b[j1] =  and (b[j2] == a[[i1] or b[j2] == .)

a = ′ a a a a ′ , b = ′ a ∗ ′ a='aaaa',b='a*' a=aaaa,b=a为例,在求状态 f [ 4 ] [ 2 ] f[4][2] f[4][2]时,先考虑*前面字符出现一次的情况,即 f [ i ] [ j − 1 ] f[i][j-1] f[i][j1]状态;再考虑*前面字符出现 n n n次的情况,此时如果匹配说明 f [ i − 1 ] [ j ] f[i-1][j] f[i1][j]也是匹配的,且匹配时*前面字符出现了 n − 1 n-1 n1次,再加上 a [ i − 1 ] = = b [ j − 2 ]   o r   b [ j − 2 ] = ′ . ′ a[i-1]==b[j-2]\ or\ b[j-2]='.' a[i1]==b[j2] or b[j2]=.的条件,足以使得 f [ i ] [ j ] f[i][j] f[i][j]匹配。

代码

#include <iostream>
#include <string>
#include <cstring>
using namespace std;
const int N = 1005;
bool f[N][N];
std::string solution(std::string a, std::string b) {
	int n = a.size();
	int m = b.size();
	memset(f,false,sizeof f);
	f[0][0] = true;
	for(int i = 1; i <= n; i++) {
		for(int j = 1; j <= m; j++) {
			if(a[i-1]==b[j-1] || b[j-1]=='.') f[i][j] = f[i-1][j-1];
			else if(b[j-1]=='*') {
				if(b[j-2] == '.' || b[j-2] == a[i-1]) f[i][j] = f[i][j-1] | f[i-1][j];
			} else f[i][j] = false;
		}
	}
	if (f[n][m]) return "yes";
	else return "no";
}
int main() {
	std::string a;
	std::string b;
	getline(std::cin, a);;
	getline(std::cin, b);;
	std::string result = solution(a, b);
	std::cout<<result<<std::endl;
	return 0;
}

3. 蚂蚁家族

题目描述

小蚂蚁群是一个庞大的群体,在这个蚂蚁群中有n只小蚂蚁 ,为了保证所有蚂蚁在消息传送的时候都能接收到消息,需要在他们之间建立通信关系。就是要求小蚂蚁都可以通过多只或者直接联系到其他人。
已知几条小蚂蚁之间有通信关系,请问还需要再新建至少多少条关系?

输入描述:

第一行输入整数n,m;n为小蚂蚁总数;m为关系数。(1<=n,m<=1000)
以下m行每行m对整数x,y。(代表x与y有联系)

输出描述:

输出最少需要新建关系数。

输入样例:

4 3
1 2
2 3
3 4

输出样例:

0

分析

这道题数据也卡了我不少时间,第一印象是求连通块的数量,如果有 r e s res res个连通块,那么需要新建的关系数就是 r e s − 1 res-1 res1,然后写了个dfs求出连通块的数量,很快写完,提交通过六成用例。调大数据范围依旧只通过六成用例,于是怀疑是否是TLE。想到使用并查集求解更为简单,遂又写了个并查集,求集合的个数,写完提交还是通过了六成用例。后面就暂时做T4了,好在T4比较简单,很快AC回过头再看本题。并查集没有AC说明不是TLE,有可能是蚂蚁的编号会出现大于n的情况。

图论问题一般默认节点编号在1到n之间,但是本题偏偏就卡了这点,解决办法也很简单,并查集建立集合和最后统计集合数量以最大值1000为基准,最后统计新建关系数时减去(1000 - n)即可。修改后提交成功AC。

代码

#include <iostream>
#include <string>
#include <cstring>
#include <sstream>
#include <vector>
#include <set>
using namespace std;
int fa[1005];
int find(int x) {
	if(x!=fa[x]) fa[x] = find(fa[x]);
	return fa[x];
}
int main() {
	int n;
	int m;
	std::cin>>n;
	std::cin>>m;
	int a,b;
	for(int i = 1; i <= 1000; i++) fa[i] = i;
	for(int i = 0; i < m; i++) {
		cin>>a>>b;
		if(fa[a] !=fa[b]) {
			fa[find(a)] = find(b);
		}
	}
	set<int> s;
	for(int i = 1; i <= 1000; i++) {
		fa[i] = find(i);
		s.insert(fa[i]);
	}
	int res = s.size() - 1 - (1000 - n);
	cout<<res<<endl;
	return 0;
}

4.题目名称:小股炒股

题目描述

已知n天后的股票行情,现在已有的本金是m,
规定只能入手一次股票和抛售一次股票。
最大收益是?

输入描述:

第一行输入整数n,m。(1<=n<=1000,1<=m<=10000)
第二行输入n个整数表示某股票单股价格p。(1<=p<=1000)

输出描述:

输出小最大收益

输入样例:

2 4
3 7

输出样例:

8

分析

股票买卖问题有多种变形,本题之前应该没有刷到过,引入了本金这个变量,难度不大。需要用到DP的思想,但是不需要真正的去DP。设 f [ i ] f[i] f[i]表示在第 i i i天卖出股票获得的最大收益,则题目的解就是 m a x ( f [ i ] ) max(f[i]) max(f[i])

既然确定了在第 i i i天卖出,那么还需要考虑的就是在哪一天入手,必然要在前面最低价入手才能获得最大的收益,所以需要求出第 i i i天前面的最低价格,平时单调栈写习惯了考试时就写了个单调栈维护,其实只需要用一个变量维护当前遍历的最小值 t t t即可,只要前面的最低价 t t t小于本金 m m m,就可以买入,设第 i i i天的价格是 x x x,则在第 i i i天卖出股票获得的最大收益是 m / t ∗ ( x − t ) m / t * (x - t) m/t(xt)。这里要注意可能不止买一手,应该按照最大的手数去买,才能获得最大的利润。

代码

#include <iostream>
#include <string>
#include <sstream>
#include <algorithm>
#include <vector>
using namespace std;
int solution(int n, int m, std::vector<int>& vec) {
	int res = 0;
	int t = 1e9;
	for(auto x : vec) {
		if (t <= m) {
			int p = m / t * (x - t);
			res = max(res,p);
		}
		t = min(t,x);	
	}
	return res + m;
}
int main() {
	int n;
	int m;
	std::vector<int> vec;
	std::cin>>n;
	std::cin>>m;
	std::string line_0, token_0;
	getline(std::cin >> std::ws,line_0);
	std::stringstream tokens_0(line_0);
	while(std::getline(tokens_0, token_0, ' ')) {
		vec.push_back(std::stoi(token_0));
	}
	int result = solution(n, m,vec);
	std::cout<<result<<std::endl;
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值