2024牛客寒假算法基础集训营1

A DFS搜索

题目描述


最近,fried-chicken完全学明白了DFS搜索(如上图所示)!于是学弟向他请教DFS搜索,fried-chicken热心的进行了讲解:
所谓DFS搜索,就是给定一个字符串sss,问能否找到sss的一个子序列,使得该子序列的值为 DFS 或 dfs。
请你分别判断字符串sss中是否含有 DFS 子序列与 dfs 子序列。
子序列的定义:从原字符串中选择一些字符,将这些字符按照其在原串中的顺序拼接起来,得到的就是原字符串的一个子序列。例如:ABCDA的子序列可以为ACA、ABCDA、BA等等,但不能为ABE、CBA、AAD。

输入描述:
输入的第一行包括一个正整数 T(1≤T≤100) ,表示测试用例的组数。

对每组测试用例,第一行是一个正整数 n(1≤n≤50) ,表示输入字符串的长度。第二行是一个长度为n的字符串s,保证字符串中只含有英语小写字母与英语大写字母。
输出描述:
对于每组测试用例,输出空格分隔的两个数字,第一个数字表示是否含有 DFS 子序列,第二个数字表示是否含有 dfs 子序列。输出 1 表示含有,输出 0 表示不含有。

分析

语法签到题

代码

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

void solve(){
	int n;cin>>n;
	string s;cin>>s;
	int a=0,b=0,f=0;
	for(int i=0;i<n;i++){
		if(s[i]=='D'){
			a=1;
		}
		if(s[i]=='F'&&a){
			b=1;
		}
		if(s[i]=='S'&&a&&b){
			f=1;
		}
	}
	cout<<f<<" ";
	a=0,b=0,f=0;
	for(int i=0;i<n;i++){
		if(s[i]=='d'){
			a=1;
		}
		if(s[i]=='f'&&a){
			b=1;
		}
		if(s[i]=='s'&&a&&b){
			f=1;
		}
	}
	cout<<f<<" ";
	cout<<'\n';
}

int main()
{
	ios::sync_with_stdio(0),cin.tie(0);
	int t;cin>>t;
	while(t--)solve();
	return 0;
}

M 牛客老粉才知道的秘密

题目描述

现在,在本次比赛的主页点击"排名",您就会看到本场比赛的榜单,可以看到,榜单中直接列出了本场比赛的所有题目。
现在,作为牛客老粉,炸鸡想用这道题给大家科普一下牛客以前榜单的愚蠢之处:
牛客以前的榜单并不是现在这样,而是至多同时只显示六道题目。同时榜单上还有"向左"按钮与"向右"按钮来切换显示的题目。以"向右"按钮为例,点击一次该按钮会显示接下来的六道题,特别的,如果接下来的六道题超出了总题数,则会将最后一题放到当前显示的最右侧。"向左"按钮同理。
现在,你需要回答,对于n道题的一场比赛,显示的六道题目中最左侧的题目一共有几种可能取值。
以下面共n=14道题的情况为例:


初始时,显示了 A 到 F;点击一次"向右",显示了 G 到 L;再点击一次"向右",此时由于剩余题数不足六题,显示的六道题是 I 到 N;此时不能继续点击"向右",点击一次"向左",显示的六道题是 C 到 H;再点击一次"向左",由于剩余题数不足六题,显示的六道题是 A 到 F。

上述过程中,显示的六道题中,最左侧的题目编号分别是 A、G、I、C、A,因此答案为 4。
输入描述:
输入的第一行包括一个正整数 T(1≤T≤105) ,表示测试用例的组数。
每组测试用例输入一个正整数 n(6≤n≤109) ,表示本场比赛的总题目数。
输出描述:
对每组测试用例,输出一个整数,表示显示的六道题目中最左侧的题目一共有几种可能取值。

分析

观察到,当n能是6的倍数时候,每次返回去都会与之前来的首位置相同,所以取值就只有从左往右的种数。

否则,每次返回去的位置一定不会与过来的首位置相同,所以返回去的数量就是过来的数量,答案就是从左往右的种数的两倍。

代码

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

void solve(){
	int n;cin>>n;
	if(n%6==0)
		cout<<n/6<<'\n';
	else
		cout<<n/6*2<<'\n';
}

int main()
{
	ios::sync_with_stdio(0),cin.tie(0);
	int t;cin>>t;
	while(t--)solve();
	return 0;
}

G why买外卖

题目描述

鸡很饿,鸡要吃外卖,今天点份炸鸡外卖!

鸡使用的外卖程序有若干个满减优惠,第iii个优惠可以表示为"满 ai ​元减 bi​ 元",多个满减优惠可以叠加。

满减的具体结算流程是:假设鸡购买的食物原价共为 x 元,则所有满足 x≥ai 的满减优惠都可以一起同时被使用,优惠后价格记为 y ,则鸡只要支付 y 元就可以了(若 y≤0 则不需要支付)。

现在,鸡的手机里一共只有 m 元钱,鸡想知道,他所购买的食物原价 x 最多为多少。
输入描述:
输入第一行包括一个整数 T(1≤T≤104) ,样例组数。

对于每组样例,第一行输入两个整数 n,m(1≤n≤1e5,1≤m≤1e9) ,含义如题面所述。接下来的n行,每行输入两个正整数 ai,bi(1≤ai,bi≤109) ,表示一个满减优惠。

保证所有样例的 Σn≤1e5 。
输出描述:
对每组用例,输出一个整数,表示鸡能购买的食物原价x最多为多少。

分析

首先对于一个满减 ,满 a1 减 b1 ,如果另外一个满减,满 a2 减 b2 ,如果满足 a2>=a1,那么如果购买的金额 大于 a2 的时候,所获得的减免就是 b1+b2 ,于是我们想到先减免要求价格从小到大排序一下,利用前缀和记录 bi ,于是便获得了大于某个价格的最大减免,由于要求得是能购买的最大值,于是我们可以从减免要求价格从大往下开始遍历,如果当前价格减去减免额之后小于给定的 m ,说明是合法的,最终的原价就是 m+ 减免额

别忘记开long long

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e5+10;
int sum[N];
struct node{
	int a,b,sum;
}p[N];

bool cmp1(node p,node q){
	if(p.a==q.a)
		return p.b>q.b;
	return p.a<q.a;
}

int n,m;
void solve(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>p[i].a>>p[i].b;
	}
	sort(p+1,p+1+n,cmp1);
	for(int i=1;i<=n;i++){
		p[i].sum=p[i-1].sum+p[i].b;
	}
	for(int i=n;i>=1;i--){
		//cout<<p[i].a<<" "<<p[i].sum<<"\n";
		if(p[i].a-p[i].sum<=m){
			cout<<p[i].sum+m<<"\n";return;
		}
	}
	cout<<m<<'\n';
}

signed main()
{
	ios::sync_with_stdio(0),cin.tie(0);
	int t;cin>>t;
	while(t--)solve();
	return 0;	
}

C 按闹分配

办事大厅目前有n个人和一个办事窗口,每个人都要在这个窗口办事,第i个人办事所需时间为 ti 。

时刻0所有人都进入办事大厅,第iii个人的不满意度 Di ​定义为他的事情办完的那个时刻。定义所有人的总不满意度 S=Σi=1nDi 。

办事处工作人员会合理安排办事顺序,使得总不满意度最小,记为 Smin ​。

现在,很急的鸡来办事了,鸡可以在任意时刻要求工作人员放下手头的事情,立刻来处理鸡的事情,鸡的事情需要 tc 时间处理完成。假设鸡插队后其余n人的总不满意度最小值变为 Sc ,若 Sc−Smin≤M ,则工作人员将允许鸡的插队,否则工作人员将拒绝。M是工作人员的容忍限度。

现在,请你回答Q组询问,即当工作人员的容忍限度为M时,鸡最早能在哪个时刻办完事。

输入描述:

第一行输入三个正整数 n,Q,tc(1≤n,Q≤105,1≤tc≤109) ,含义如题面所述。

第二行输入n个正整数 ti(1≤ti≤106) ,表示第i个人办事所需时间。

接下来Q行,每行一个正整数 M(0≤M≤1018) ,表示该组询问中工作人员的容忍度。

分析

首先对于不满意度,其实就是等待的时间越短,不满意度越低,于是我们考虑把办事所需时间较小的排在前面。然后通过前缀和计算出这个不满意度。

然后我们考虑不满意度,当我们从某个位置插入一个 t 的时候,他花费的时间为 tc ,对于他插入后后面的人,办事的时间就会往移动 tc ,假设后面有 x 个人,每个人都增加 tc ,那么总共不满意度就会增加 x⋅tc 即 Sc=Smin+x∗tc ,于是根据题目得出 x⋅tc<=M ,那么对于最大值, x=M÷tc ,于是知道了后面有几个人,那么总人数减去后面有几个人就知道了前面有几个人,于是便可以求出答案就是 sum[n−x]+tc

代码

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int t[N],s[N];

int main()
{
	ios::sync_with_stdio(0),cin.tie(0);
	int n,q,tc;cin>>n>>q>>tc;
	for(int i=1;i<=n;i++)
		cin>>t[i];
	sort(t+1,t+1+n);
	for(int i=1;i<=n;i++)
		s[i]=s[i-1]+t[i];
	while(q--){
		int m;cin>>m;
		int id=min(n,m/tc);
		cout<<s[n-id]+tc<<'\n';	
	}
	return 0;
}

E 本题又主要考察了贪心

题目描述

斗鸡联赛一共有n只鸡参加,第i只鸡截至目前已经积了 ai​ 分。 接下来还有m场比赛要进行,第i场比赛的对阵双方是编号为 ui​ 和 vi​ 的鸡。积分规则是:胜方加三分,败方不得分,若战平则双方各得一分。 请你计算在最好的情况下,我们的一号选手(炸鸡)能够排到第几名。 注意若有多鸡并列,则排名取并列的排名,且不影响随后的排名(例如两只鸡并列第二名,则都视为第二名,排名其后的下一只鸡视为第四名)。 输入描述: 输入第一行包括一个整数 T(1≤T≤100) ,样例组数。 对于每组样例 第一行输入两个整数 n,m(2≤n≤10,1≤m≤10) ,含义如题面所述。 第二行输入n个整数 ai(0≤ai≤100) ,表示第iii只鸡当前已经有的积分。 接下来的m行,每行有两个正整数 ui,vi(1≤ui,vi≤n,ui≠vi) ,表示第i场比赛的对阵双方。 输出描述: 对每组样例,输出一个整数表示一号选手最好的情况下能够排到第几名。

分析

首先观察到数据的范围是非常小的,于是我们可以想一下暴力的做法。由于每次都会有三种情况,于是我们可以利用dfs暴力的枚举每一种情况。注意我们在搜索之后要记得恢复成搜索之前的情况,这样我们才能搜完全不漏。具体实现看下述代码。

代码

#include<bits/stdc++.h>
using namespace std;
const int N=15;
int a[N],b[N],c[N];
int ans;
int n,m;

void dfs(int u){
	if(u==m+1){//递归终点
		int rk=1;
		for(int i=2;i<=n;i++)
			if(a[1]<a[i])
				rk++;
		ans=min(rk,ans);
		return;
	}
	a[b[u]]+=3;
	dfs(u+1);
	a[b[u]]-=3;
	a[c[u]]+=3;
	dfs(u+1);
	a[c[u]]-=3;
	a[b[u]]++,a[c[u]]++;
	dfs(u+1);
	a[b[u]]--,a[c[u]]--;
}

void solve(){
	cin>>n>>m;
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=1;i<=m;i++)cin>>b[i]>>c[i];
	ans=n;
	dfs(1);
	cout<<ans<<'\n';
}

int main()
{
	ios::sync_with_stdio(0),cin.tie(0);
	int t;cin>>t;
	while(t--)solve();
}

G 要有光

神说要有光,于是就有了本题。

分析

代码

#include<bits/stdc++.h>
using namespace std;
void  solve(){
    double c,d,h,w;
    cin>>c>>d>>h>>w;
    cout<<3*c*w<<'\n';
}
int main(){
    int t;cin>>t;
    while(t--)solve();
}

B 关鸡

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
typedef pair<int,int>PII;

void solve(){
	int n;cin>>n;
	if(n==0){
		cout<<3<<'\n';return;
	}
	int f1=0,f2=0,f3=0,f4=0,f5=0;//左边有没有火,右边有没有火,左边有没有被封死,右边有没有别封死,是否存在(2,0)点
	vector<int>s,u;
	int cnt1=0,cnt2=0,cnt3=0,ans;
	for(int i=1;i<=n;i++){
		int x,y;cin>>x>>y;
		if(x==1){//上一行
			s.push_back(y);
		}
		else{//第二行
			u.push_back(y);	
		}
		if(y<0)f1=1;//左边
		if(y>0)f2=1;//右边
		if(x==2&&y==0)
			f5=1,cnt3++;
		if(x==1&&y==-1)
			cnt1++;
		if(x==1&&y==1)
			cnt2++;
	}	
	sort(s.begin(),s.end());
	sort(u.begin(),u.end());
	int j=0;
	for(int i=0;i<s.size();i++){
		while(j+1<u.size()&&s[i]+1>=u[j+1])j++;
		if(j<u.size()&&j>=0&&abs(s[i]-u[j])<=1){
			if(s[i]<0)
				f3=1;
			else
				f4=1;
		}
	}
	
	ans=3-cnt1-cnt2-cnt3;
	if(f3&&f4)//左右都被堵死
		ans=0;
	else if(f3){//只有左边被堵死
		if(f2||f5)ans=min(ans,1);
		else ans=min(ans,2);
	}
	else if(f4){
		if(f1||f5)ans=min(ans,1);
		else ans=min(ans,2);
	}
	else{
		if(f1&&f2)ans=min(ans,2);
		else ans=min(ans,3);
	}
	cout<<ans<<'\n';
	//cout<<f1<<' '<<f2<<" "<<f3<<" "<<f4<<" "<<f5;
	//cout<<'\n';
}

int main()
{
	ios::sync_with_stdio(0),cin.tie(0);
	int t;cin>>t;
	while(t--)solve();
	return 0;
}

H 01背包,但是bit

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
#define int long long
int v[N],w[N];
int n,m;
int ans;

void calc(int s){
	int res=0;
	for(int i=1;i<=n;i++)
		if((s&w[i])==w[i])//表示当前值s小于w,那么就是可以选择的
			res+=v[i];
	ans=max(ans,res);
}

void solve(){
	cin>>n>>m;
	ans=0;
	for(int i=1;i<=n;i++)cin>>v[i]>>w[i];
	calc(m);//先枚举合法方案当前位置也是1的情况
	for(int i=29;i>=0;i--){
		if((m>>i)&1){//如果当前位置是1,那么只要数字当前位是0,那么后面的就不会有任何限制
			calc((m^(1<<i))|((1<<i)-1));
		}
	}
	cout<<ans<<'\n';
}

signed main()
{
	ios::sync_with_stdio(0),cin.tie(0);
	int t;cin>>t;
	while(t--)solve();
}

F 鸡数题

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e5+10,md=1e9+7;
int fac[N],inv[N];

int qmi(int a,int b){
	int res=1;
	while(b){
		if(b&1)res=res*a%md;
		b>>=1;
		a=a*a%md;
	}
	return res;
}

void init(){
	int m=1e5+5;
	fac[0]=1;
	for(int i=1;i<=m;i++)
		fac[i]=fac[i-1]*i%md;
	inv[m]=qmi(fac[m],md-2);
	for(int i=m;i>=1;i--)
		inv[i-1]=inv[i]*i%md;
}

int C(int n,int m){
	return fac[n]*inv[m]%md*inv[n-m]%md;
}

signed main()
{
	ios::sync_with_stdio(0),cin.tie(0);
	int n,m;cin>>n>>m;
	init();

	int ans=0;
	for(int i=0;i<=m;i++){
		if(i%2==0)
			ans=(ans+C(m,i)%md*qmi(m-i,n)%md*inv[m]%md)%md;
		else
			ans=(ans-C(m,i)%md*qmi(m-i,n)%md*inv[m]%md+md)%md;
	}
	cout<<ans<<'\n';
}

I It's bertrand paradox. Again!

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


int main()
{
	ios::sync_with_stdio(0),cin.tie(0);
	int n;cin>>n;
	int cnt=0;
	while(n--){
		int x,y,r;cin>>x>>y>>r;
		if(max(x,y)>=95)cnt++;
	}
	if(cnt>=500)cout<<"bit-noob";
	else cout<<"buaa-noob";
	return 0;
}

  • 40
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值