LDU 2022年2021级测试赛-1

我写的第一篇博客 好好写,好好写

A 运动会

模拟了一下,但实在没思路,等以后做出来回来补上.

B 有几个ACM

题目描述:有一群人非常喜欢ACM 比赛,只要是跟ACM 相关的东西他们都非常在意。有一天,他们在鲁东大学的校园里看到了一个仅由”A”、”C”、”M”这三个字符组成的横幅。一时兴起,他们想要知道这个横幅里出现了几次”ACM”,你也是其中的一员,请编写一个程序解决这个问题。

输入:输入只有一行,包含一个字符串s(length(s)≤1e5 ),只含有A、C、M 这三种字母。

输出:在一行中输出给定的字符串中含有多少个ACM。最终的结果可能比较大,请对结果用1000000007 取余。

思路:首先看到字符串的范围 1e5 三重for去搜必然超时 ,即使是两重for 也会超时 所以要找时间复杂度为O(n)的思路;

O(n)思路:  我们在 s 中去找ACM这个子串,只有找到M才会计数,比如AACCM,只会在最后的M记一次数,而此时要记的数应该是前面AC这个串的个数(即把找ACM的问题分解成找AC的个数),我们不妨再分解一次,即为找字串"A"的个数。遇到"C"把A的个数加到C上去,遇到M把C的个数加到M上去,这就是O(n)的思路,知道了思路后就可以找更长的字串,不断分解就是了;

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const ll maxx = 1e18;
const int N = 1e6+10;
const ll p = 1000000007;
const double eps = 1e-8;
string s;
int len,cnta,cntc;

ll sum;
int main()
{
	cin>>s;
	
	len=s.size();
	
	for(int i=0;i<len;i++)
	{
		if(s[i]=='A')
			cnta++;
		else
		if(s[i]=='C')
			cntc+=cnta;
		else
			sum=(sum+cntc)%p;
	}
	
	
	cout<<sum%p;
	
	return 0; 
}

C  小张的困惑(dfs)

题目描述:最近,一直在IT行业"搬砖"的程序员小张考虑到自己日渐稀疏的发量,决定跳槽去做工地"搬砖"做一名真正的民工,可是小张进入工地的第二天,包工头就交代给了小张另一个对发量极为不利的烧脑任务。现在有MM(1≤M≤20)块工地的石材,高度分别Hi米(1≤Hi≤1000,000),这些石材的垒起来的总高度为S,包工头让小张从这些石材中选出一些石材,垒出一个工作台,要求工作台的高度不能低于N 米(1≤N),并且高出N米的长度越少越好,这样方便工人站在该工作台上作业。现在,你能帮小张从这M块石材中找出一个集合,垒起来的高度满足包工头的要求吗?如果不能满足条件,就输出能组成的最大高度。

输入:

第一行M和N 分别表示石材的个数和包工头要求的最低高度

第二到M+1M+1 行,H1,H2,…,HMH1,H2,…,HM 表示每块石材的高度

输出:

输出一个整数,表示满足包工头要求的石材垒出的高度。

思路:一看就是个  搜索 的问题 M的范围也很小,直接上dfs

#include<bits/stdc++.h>
using namespace std;

typedef unsigned long long ull;
typedef long long ll;

const ll maxx = 1e18;
const int N = 1e6+10;
const int p = 1e4;
const double eps = 1e-8;

int m,n;
int a[p];
ll l=maxx,ls,sum1;


void dfs(int t,int sum)
{
	if(sum>=n)
	{
		if(abs(sum-n)<l)
		{
			l=abs(sum-n);
			ls=sum;
		}
	}//达到要求就做一个比较
	
	
	if(t==m+1)
		return ;//当所有石块用完了要跳出
	
	dfs(t+1,sum+a[t]);
	dfs(t+1,sum);//分别模拟每一个石块的用与不用
}

int main()
{
	cin>>m>>n;
	
	for(int i=1;i<=m;i++)
	{
		cin>>a[i];
		sum1+=a[i];
	}
	
	dfs(1,0);
	
	
	if(ls==0)//如果 ls 没更新过说明没有合要求的
		cout<<sum1;
	else
		cout<<ls;

	return 0; 
}

D  光光学回文

题目描述:光光喜欢研究字符串,有一天,他得到了两个字符串a和b,它们长度相同,光光想选择一个位置,将两个字符串在相同的位置处分割开,既如下操作:a可以得到两个字符串a prefix和a suffix且满足a=aprefix+asuffix。b可以得到两个字符串b prefix和b suffix且满足b=bprefix+bsuffix。现在,光光想知道,能否选择一个下标进行分割,使得分割后的aprefix+bsuffix或bprefix+asuffix构成回文,这个问题对于光光来说太简单,你能帮他解决吗?

输入:输入字符串a,b保证1≤a.length,b.length≤1e5,仅包含小写字母

输出:输出只有一行,如果存在这样的下标,使得分割后的aprefix+bsuffix或bprefix+asuffix构成回文,输出true 否则输出false

思路:又是1e5的长度,如果是最暴力的方法,每个位置去试,然后判断两个串是否为回文串时间复杂度为O(n*n),肯定超时,所以要想巧方法;

巧方法:假如最后会构成回文的话,会有四种情况,如图

1.  a1,b2对应回文   a为回文串

2.  a1,b2对应回文   b为回文串

3.  a2,b1对应回文   a为回文串

4.  a2,b1对应回文  b为回文串

因此我们只要模拟这四种情况,即先找是否有前后公共部分,然后判断剩余部分是否回文即可

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const ll maxx = 1e18;
const int N = 1e6+10;
const int p = 1e4;
const double eps = 1e-8;

string s1,s2; 
int len,cnt,cnt1;

int judge(int cnt,string s)
{
	int len=s.size();
	for(int i=cnt,j=len-1-cnt;i<=j;i++,j--)
	{
		if(s[i]!=s[j])
			return 0;	
	}
	return 1;
}

int main()
{
	cin>>s1>>s2;
	
	len=s1.size();
	
	for(int i=0,j=len-1;i<len;i++,j--)
	{
		if(s1[i]==s2[j])
			cnt++;
		else
			break;
	}
	
	for(int i=0,j=len-1;i<len;i++,j--)
	{
		if(s2[i]==s1[j])
			cnt1++;
		else
			break;
	}
	
	if(judge(cnt,s1)||judge(cnt,s2)||judge(cnt1,s1)||judge(cnt1,s2))
		cout<<"true";
	else
		cout<<"false";
	
	
	return 0; 
}

E  健忘的光光

题目描述:

光光喜欢研究字符串,有一天,他想了n 个长度为K的由小写字母组成字符串A1,A2,…,AnA1,A2,…,An当作自己的密码,为了避免忘记,光光把它们藏起来了,于是创造了S1,S2,…,SnS1,S2,…,Sn这样nn 个字符串,并且Ai是Si的子串.为了避免自己看到Si的时候也想不起自己设的密码,光光把A1,A2,…,AnA1,A2,…,An按顺序藏在了字符串TT 中,也就是说TT 是C1A1C2A2…CnAnCn+1C1A1C2A2…CnAnCn+1。其中对于1≤i≤N+11≤i≤N+1, Ci可以是空串。

但是不幸的是,光光还是忘记了密码。现在,有S1,S2,…,SnS1,S2,…,Sn和T,你能帮光光超计算密码长度K 的最大值可能是多少吗?

输入:

输入包含多个测试文件,每个测试文件包含单组测试数据。

第一行是字符串T。

第二行是一个整数nn(1≤n≤10) 。

第3 至N+2 行,每行一个字符串,分别表示S1,S2,…,SN。

字符串由小写字母组成。

保证T 是长度不超过250000,Si 的长度之和不超过25000

输出:一行一个整数,输出KK 的最大值,如果无解输出-1

没思路,等以后回来再来补上

F  搬砖的小张要秃头(贪心)

题目描述:最近,一直在IT 行业"搬砖"的程序员小张考虑到自己日渐稀疏的发量,决定跳槽去做工地"搬砖"做一名真正的民工,可是小张进入工地的第一天,包工头就交代给了小张一个对发量极为不利的烧脑任务。现在有M(1≤M≤20000)块工地的石材,高度分别Hi米(1≤Hi≤10000),这些石材的垒起来的总高度为SS,包工头让小张从这些石材中选出一些石材,垒出一个工作台,要求工作台的高度不能低于NN 米(1≤N≤S),且石材的数目尽可能少。现在,你能帮小张从这M 块石材中找出一个集合,垒起来的高度满足包工头的要求吗?

输入:

输入包含两行:

第一行M 和N 分别表示石材的个数和包工头要求的最低高度

第二到M+1M+1 行,H1,H2,…,HMH1,H2,…,HM 表示每块石材的高度

输出:输出一个整数,表示垒出不低于NN 米的最少石材数目。

思路:贪心问题,排序后每次都垒最高的即可

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const ll maxx = 1e18;
const int N = 1e6+10;
const int p = 1e4;
const double eps = 1e-8;

int m,n,sum,cnt;
int a[N];

int main()
{
	cin>>m>>n;
	
	for(int i=1;i<=m;i++)
		cin>>a[i];
	
	sort(a+1,a+1+m);
	
	for(int i=m;i>=1;i--)
	{
		sum+=a[i];
		cnt++;
		if(sum>=n)
			break;
	}
	
	cout<<cnt;
	
	return 0;
}

G  圣诞爷爷的礼物

题目描述:圣诞节快要到了,圣诞老爷爷要打包nn 份糖果分给小朋友们,假设圣诞老爷爷已经打包好了m份糖果了。恰好轮到小明了,小明因为是里面最小的小朋友,所以小明可以要两份,并且可以提出要求,小明希望能分到这n 份糖果中最多糖果的一份和最少糖果的一份,并且里面的糖果恰好为a 和b 个,这可难到圣诞老爷爷了,打包好的不可以拆开,剩下的n−m 份都可以现装糖果,问能否满足小明的要求。

输入:

输入包含两行:

第一行输入n,m,a,b, 其中a 和b 的大小关系不确定。

第二行表示已经打包好的m 份糖果各自数量1≤n,m,a,b≤1000,m≤n

输出:输出YES 或者NO

思路:首先把所有的情况分为三类 第一种是 n=m 的情况 第二种是 n-m=1 的情况 第三种是 n-m>1的情况,然后分别处理(注意处理a ,b的关系)

1. n = m

首先所有已包好的糖果如果出现大于最大值或者小于最小值的不满足;

其次找最大值和最小值在已包好的里面是否都出现了,都出现了就满足,否则不满足

2. n - m = 1

首先所有已包好的糖果如果出现大于最大值或者小于最小值的不满足;

其次找最大值和最小值在已包好的中是否出现任意一个,只要出现一个就满足要求(只要出现一个另一个可以去包装,都出现更好),否则不满足;

3. n - m > 1

只要已包好的糖果没有大于最大值或者小于最小值的情况就满足,否则不满足;

#include<bits/stdc++.h>
using namespace std;

typedef unsigned long long ull;
typedef long long ll;

const ll maxx = 1e18;
const int N = 1e6+10;
const int p = 1e4;
const double eps = 1e-8;

int n,m,a,b,sum;// a 小  b  大 
int max1,min1=1e6;

int main()
{
	cin>>n>>m>>a>>b;
	sum=a+b;
	a=min(a,b);
	b=sum-a;
	
	if(n==m)
	{
		int flag1=0,flag2=0;
		for(int i=1;i<=m;i++)
		{
			cin>>sum;
			if(sum<a||sum>b)
			{
				cout<<"NO";
				return 0;
			}
			if(sum==a)
				flag1=1;
			else
			if(sum==b)
				flag2=1;
		}
		
		if(flag1==1&&flag2==1)
			cout<<"YES";
		else
			cout<<"NO";
		return 0;		
	}
	
	if(n-m==1)
	{
		int flag1=0,flag2=0;
		for(int i=1;i<=m;i++)
		{
			cin>>sum;//sum只是个临时变量,没有任何含义
			if(sum<a||sum>b)
			{
				cout<<"NO";
				return 0;
			}
			if(sum==a)
				flag1=1;
			else
			if(sum==b)
				flag2=1;
		}
		
		if(flag1==1||flag2==1)
				cout<<"YES";
		else
				cout<<"NO";
		return 0;
	}
	
	for(int i=1;i<=m;i++)
	{
		cin>>sum;
		if(sum<a||sum>b)
		{
			cout<<"NO";
			return 0;
		}
	} 
	
	cout<<"YES";
	
	
	
	return 0;
}

H  套娃查询(前缀和优化+记忆递归)

题目描述:

你喜欢套娃,根本停不下来。为了终止你的套娃行为,算法之神定义了两个函数ff 和gg(如下图所示)使得你的套娃行为最终停止下来。

为了惩罚你的套娃行为,算法之神交给了你一项任务:

你需要进行Q 次查询。在每一次查询中,你将被给予三个数字l,r 和k。你需要在区间[l,r]上找到满足公式g(x)=k 的x 的数量(l≤x≤r)。

 输入:

第一行给出整数Q( 1≤Q≤2∗1e5)表示有Q 次查询。

接下来的Q 行,每行包括三个整数l,r 和k(1≤l≤r≤1e6 ,1≤k≤9)

输出

对于每一次查询,输出单独一行,表示本次查询的结果。

思路:询问次数最大来到了恐怖的2*1e5 次 每次都调用递归的话必然超时,每次询问要优化到O(1)的时间复杂度才可以,因此选择用前缀和优化,b[ i ][ j ]数组代表前 i 个数里 j 的个数,

对于每次询问 l r k 答案就是 b[ r ][ k ] - b[ l-1 ][ k ] 注意是 l - 1;

记忆递归 :对于每次询问每次递归,可能出现重复情况用记忆递归优化

代码如下

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const ll maxx = 1e18;
const int N = 1e6+10;
const int p = 1e4;
const double eps = 1e-8;


int a[N];
int n;
int l,r,k,cnt;
int b[N][10];


int check(int n)
{
	if(a[n]==0)
	{
		if(n<10)
			a[n]=n;
		else
		{
			int n1=n,sum=1;
			while(n1)
			{
				if(n1%10!=0)
				sum=sum*(n1%10);
				n1/=10;
			}
			a[n]=check(sum);
		}
	}
	
	return a[n];
}

int main()
{
	
	for(int i=1;i<=1e6;i++)
	{
		for(int j=1;j<=9;j++)
		{
			b[i][j]=b[i-1][j];	
		}
		b[i][check(i)]++;		
	}
	cin>>n;
	
	while(n--)
	{
		scanf("%d %d %d",&l,&r,&k);
		printf("%d\n",b[r][k]-b[l-1][k]);
		
	}
	
	
	return 0;
}

I  天气之子

题目描述:

一位拥有操作天气超能力的少女说道:“呐,现在开始就要放晴了哦~”

于是倾盆大雨停止了,天空放晴了……

她发现一些路边的石柱接住了一些雨水,她很好奇,这些石柱接住了多少雨水呢?如下图所示,这是n 个石柱的侧视图,请计算按此排列的石柱接住了多少雨水(黑色为石柱,蓝色为雨水)。

输入:

第一行,一个非负整数n(0≤n≤3×1e4),表示有n 个石柱。

第二行,n 个非负整数组成的序列,表示石柱的高度height[i](0≤height[i]≤1e5,0≤i<n)。

输出:输出只有一行,包含一个非负整数num 表示石柱可以接住的雨水量。

思路:一开始看这个题的时候被这个图给误解了,第一次的思路是找所有相对高的点(一个点同时高于相邻两个点即为相对高的点),然后算相对高点之间点的贡献,但是错了,给个图解释下为什么错了,顺便引出正确思路

按照错误思路就是 先找 1 2之间点的贡献,再找2 3之间点的贡献 但是明显看出应该找1 3之间的贡献才对,所以每个点的贡献不是左右相对高点决定的,而是左右最高点决定的

因此每个点的贡献表示为   min(lmax[ i ],rmax[ i ])-a[ i ],当然了只有当这个贡献为正的时候才把它加上

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const ll maxx = 1e18;
const int N = 1e5+10;
const int p = 1e4;
const double eps = 1e-8;

int n,max1;
int a[N];
int lmax[N];
int rmax[N],sum;

int main()
{
	cin>>n;
	
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
	}
	
	for(int i=1;i<=n;i++)
	{
		lmax[i]=max1;
		max1=max(max1,a[i]);
	}
	max1=0;
	
	for(int i=n;i>=1;i--)
	{
		rmax[i]=max1;
		max1=max(max1,a[i]);
	}
	
	for(int i=1;i<=n;i++)
	{
		if((min(lmax[i],rmax[i])-a[i])>0)
			sum+=(min(lmax[i],rmax[i])-a[i]);
		
	}
	
	cout<<sum;
	
	
	return 0;
}

 J . 大乌龟冲呀!(线性dp)

题目描述:在杂志社工作的小鹏在休息日突然接到老板丢过来的文本编辑任务,要求将一串漏洞百出的文本A 编辑为小鹏认为合理的文本B,小鹏一次可以对一个字符进行插入、删除和替换操作,每种操作需要耗费的时间分别对应为x,y,z分钟,然而小鹏一心只想看T 分钟后的英雄联盟S10 全球总决赛,亲眼见证最喜欢的大乌龟战队夺冠。问:小明是否可以准时观看比赛?若可以,请告诉小明完成工作后还有多久开始比赛,否则输出比赛已经开始了多久。

输入:

A

B

X Y Z T

输出

Yes (or No) 和一个整数(如果时间准时看比赛,请输出Yes 0)

思路:当时没有思路,结束了之后去研究了一下学姐的思路,是一个动态规划问题

先上状态转移方程

            if(s1[i-1]==s2[j-1])
                dp[i][j]=min(dp[i-1][j-1],min(dp[i-1][j]+y,dp[i][j-1]+x));
            else
                dp[i][j]=min(dp[i-1][j-1]+z,min(dp[i-1][j]+y,dp[i][j-1]+x));

如果第 A 的 第 i 位 与 B 的 第 j 位 相等  有三种情况 

1.不做处理  权值不变 dp[i][j]=dp[i-1][j-1];

2.由前一种情况删除一位得到  dp[i][j]=dp[i-1][j]+y;

3.由前一种情况插入一位得到  dp[i][j]=dp[i][j-1]+x;

找转移方程的时候找到改变的是谁(插入在B中插入,删除在A中删除)

如果第 A 的 第 i 位 与 B 的 第 j 位 不相等  有三种情况 

1.由前一种情况替换一位得到 dp[i][j]=dp[i-1][j-1]+z;//理解为在相等不做处理的情况下多做了一次替换操作

2.由前一种情况删除一位得到  dp[i][j]=dp[i-1][j]+y;

3.由前一种情况插入一位得到  dp[i][j]=dp[i][j-1]+x;

附上洛谷的原题:https://www.luogu.com.cn/problem/P2758

附上代码

#include<bits/stdc++.h>
using namespace std;

typedef unsigned long long ull;
typedef long long ll;

const ll maxx = 1e18;
const int N = 1e6+10;
const int p = 1e4;
const double eps = 1e-8;

ll dp[6001][6001];//dp[i][j]表示把 A 的前 i 个字母编辑成 B 的 前 j 个字母消耗的最小时间 
string s1,s2; 
int len1,len2;

ll x,y,z,t;

int main()
{
	cin>>s1>>s2;
	
	cin>>x>>y>>z>>t;
	
	len1=s1.size();
	len2=s2.size();
	
	for(int i=0;i<=len2;i++) dp[0][i]=i*x;
	for(int i=0;i<=len1;i++) dp[i][0]=i*y;
	
	for(int i=1;i<=len1;i++)
	{
		for(int j=1;j<=len2;j++)
		{
			if(s1[i-1]==s2[j-1])
				dp[i][j]=min(dp[i-1][j-1],min(dp[i-1][j]+y,dp[i][j-1]+x));
			else
				dp[i][j]=min(dp[i-1][j-1]+z,min(dp[i-1][j]+y,dp[i][j-1]+x));
		}
	}
	
	if(dp[len1][len2]<=t)
		cout<<"Yes 0";
	else
		cout<<"No "<<dp[len1][len2]-t;
	
	
	
	return 0; 
}

第一次写博客,比较仓促,有什么有问题的地方欢迎大家留言讨论

  • 10
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

梦里再与你相见

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值