AtCoder Beginner Contest 364

AtCoder Beginner Contest 364

比赛地址添加链接描述

A - Glutton Takahashi

算法:模拟

算法大意

给你n个字符串,字符串只有两种,sweet,salty,如果连续吃到两个sweet,那么就无法再进行吃下去了。
判断是否能吃下所有的菜肴。

题目思路

可以通过一个ans变量来判断是否吃了连续的sweet,值得注意的是,即便吃了连续的两个sweet,如果此时已经吃完了,那么也算吃下来所有的菜肴。

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
void slove() {
	int n;
	cin>>n;
	int ans = 0;
	int flag = 0;
	for(int i=1;i<=n;++i) {
		string s;
		cin>>s;
		if(s=="sweet") ans++;
		else ans=0;
		if(ans==2 && i!=n) flag = 1;
	} 
	if(flag == 1) cout<<"No";
	else cout<<"Yes";
	return;
}
int main(){
	slove();
	return 0;
}

B - Grid Walk

算法:模拟

题目大意

在一个由 H 行和 W 列组成的网格中,每个单元格要么是空的(用字符 . 表示),要么被占用(用字符 # 表示)。高桥(一个人名)从初始位置 (Si, Sj) 开始,根据给定的指令序列 X 进行一系列移动操作。

指令序列 X 中的每个字符代表一个动作:

“L”:向左移动
“R”:向右移动
“U”:向上移动
“D”:向下移动
对于序列 X 中的每个字符 i:

如果字符是 “L” 并且左边的单元格是空的,高桥将向左移动。
如果字符是 “R” 并且右边的单元格是空的,高桥将向右移动。
如果字符是 “U” 并且上方的单元格是空的,高桥将向上移动。
如果字符是 “D” 并且下方的单元格是空的,高桥将向下移动。
如果当前单元格的相邻单元格不是空的,或者已经到达网格的边界,则高桥将留在当前单元格。

最终,需要输出高桥完成所有移动操作后所在的单元格位置。

算法思路

只需要模拟即可,只是需要特别判断一下是否越界的情况。

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
string s[110];
void slove() {
	int h,w;
	cin>>h>>w;
	int x,y;
	cin>>y>>x;
	for(int i=1;i<=h;++i) {
		string t;
		cin>>t;
		s[i]=" ";
		s[i]+=t;
	}
	string ts;
	cin>>ts;
	for(int i=0;i<ts.size();++i) {
		if(ts[i]=='L' && x-1>=1 && s[y][x-1]=='.') --x;
		if(ts[i]=='R' && x+1<=w && s[y][x+1]=='.') ++x;
		if(ts[i]=='U' && y-1>=1 && s[y-1][x]=='.') --y;
		if(ts[i]=='D' && y+1<=h && s[y+1][x]=='.') ++y;
	}
	cout<<y<<" "<<x;
	return;
}
int main(){
	slove();
	return 0;
}

C - Minimum Glutton

算法:贪心

题目大意

有 N 道不同的菜肴,每道菜由两个属性定义:甜度 Ai 和咸度 Bi。

高桥想要将这些菜肴按照自己的喜好顺序排列,然后依次吃掉。但他有两个限制条件:

总甜度不超过某个阈值 X。
总咸度不超过另一个阈值 Y。
一旦他吃掉的菜肴的总甜度超过 X 或总咸度超过 Y,他就会停止吃剩下的菜肴。

任务是求出高桥最后能够吃掉的菜肴的最少数量

算法思路

要求我们求出最少数量,那么先让a和b排序,然后我们可以只看一个阈值,如果在贪心的思想下甜度超出了X的阈值,直接输出答案,或者咸度超出了Y的阈值,直接输出答案。
有可能在遍历的过程中不会超出阈值,那么输出n即可。

代码

#include<bits/stdc++.h>
#define ll long long
const int N = 2e5+7;
using namespace std;
ll a[N],b[N];
void slove() {
	ll n,x,y;
	cin>>n>>x>>y;
	for(int i=1;i<=n;++i) cin>>a[i];
	for(int i=1;i<=n;++i) cin>>b[i];
	sort(a+1,a+n+1,greater<int>());
	sort(b+1,b+n+1,greater<int>());
	int ans = INT_MAX;
	ll tansa,tansb;
	tansa = 0;
	tansb = 0;
	for(int i=1;i<=n;++i) {
		tansa+=a[i];
		tansb+=b[i];
		if(tansa>x) {
			cout<<i;
			return;
		}
		if(tansb>y) {
			cout<<i;
			return;
		}
	}
	cout<<n;
	return;
}
int main(){
	slove();
	return 0;
}

D - K-th Nearest

算法:二分

题目大意

在一条数轴上,有 N + Q 个点,分为两组:

A 组包含 N 个点,其坐标分别为 a1, a2, …, aN。
B 组包含 Q 个点,其坐标分别为 b1, b2, …, bQ。
对于 B 组的每个点 bj(对于 j = 1, 2, …, Q),需要解决以下问题:

找到 A 组中最接近点 bj 的第 kj 个最近点,并计算这个最接近点与 bj 之间的距离。具体来说:

计算每个 A 组中的点 ai 与点 bj 之间的距离,记为 di。
将所有这些距离 d1, d2, …, dN 按升序排序,得到一个新的距离序列 (d1’, d2’, …, dN’)。
求出排序后序列中的第 kj 个距离,即 dkj’。
任务是对每个 B 组中的点 bj,计算并输出对应的 dkj’ 值。

算法思路

因为题目数据的问题,我们无法对每一次询问都遍历数组,这样会导致超时(O^2),对于这种具有单调性的序列,我们可以进行二分操作。
对于每一次询问,我们二分的查找值(并不是位置),这道题的难点便是想到二分,因为二分的check函数仍然需要使用二分进行查找,否则仍然会超时,具体的check函数思想可以看我的代码。

代码


#include<bits/stdc++.h>
#define ll long long
const int N = 1e5+7;
using namespace std;
int n,q;
int a[N];
int b,k;
bool check(int x) {
	int l = lower_bound(a+1,a+n+1,b-x)-a;
	int r = upper_bound(a+1,a+n+1,b+x)-a;
	return r-l>=k;
}
void slove() {
	cin>>n>>q;
	for(int i=1; i<=n; ++i) cin>>a[i];
	sort(a+1,a+n+1);
	while(q--) {
		cin>>b>>k;
		int l=-1,r=2e8+7;
		while (l < r) {
			int mid = l + r >> 1;
			if (check(mid)) r = mid;
			else l = mid + 1;
		}
		cout<<l<<endl;
	}
	return;
}
int main() {
	slove();
	return 0;
}

E - Maximum Glutton

算法:动态规划

题目大意

高桥准备了 N 道菜,每道菜有两个属性:甜度 Ai 和咸度 Bi。高桥可以自由地按照任何顺序排列这些菜肴。

斯助将按照高桥排列的顺序吃这些菜肴。但是,有两个限制条件:

斯助不会吃总甜度超过 X 的菜肴。
斯助不会吃总咸度超过 Y 的菜肴。
一旦斯助吃的菜肴的总甜度超过 X 或总咸度超过 Y,他将停止吃任何更多的菜肴。

高桥的目标是让斯助吃掉尽可能多的菜肴。任务是求出在最理想的情况下,斯助最多能吃掉多少道菜。

算法思路

一开始我想到通过dp[i][j][k]来实现的,dp表示的是前i道菜肴j的甜度k的咸度的最多吃掉的菜肴数,但是因为X,Y的取值范围是(1<X,Y<10000),这样的时间复杂度大概是(801000010000),会超时,那么就可以通过交换一种典型思想,交换dp的键和值。
这样的话dp[i][j][k]表示的就是前i道菜肴吃了j道菜k的甜度时的最小咸度,这样更新完dp以后,只需要找到第一个小于Y的数即可。

代码

这是错误思想的代码,会超时

#include<stdio.h>

short n,x,y;
short a[107];
short b[107];
int adda,addb;
short dp[10007][10007];
short max(short x, short y) {
    return (x > y) ? x : y;
}
int main() {
	scanf("%hd %hd %hd", &n, &x, &y);
	for(int i=1; i<=n; ++i) {
		scanf("%hd %hd", &a[i], &b[i]);
		adda+=a[i],addb+=b[i];
	}
	if(adda<=x && addb<=y) {
		printf("%hd\n", n);
		return 0;
	}
	
	//第i个菜肴
	for(short i=1; i<=n; ++i) {
		//甜度为ta
		for(int ta=x; ta>=a[i]; --ta) {
			//咸度为tb
			for(int tb=y; tb>=b[i]; --tb) {
				//能吃
				dp[ta][tb]=max(short(dp[ta-a[i]][tb-b[i]]+1),dp[ta][tb]);
			}
		}
	}
	printf("%hd\n", dp[x][y] + 1);
	return 0;
}

正确思路的代码

#include<bits/stdc++.h>
using namespace std;
//菜肴数要少的多
//我们可以dp定义前i道菜肴吃了j道的最小咸度
int n,x,y;
int a[87];
int b[87];
int adda,addb;
int dp[87][87][10007];
int main() {
	for (int i = 0; i < 87; ++i) {
		for (int j = 0; j < 87; ++j) {
			for (int k = 0; k < 10007; ++k) {
				dp[i][j][k] = 1e9+7;
			}
		}
	}
	scanf("%d %d %d", &n, &x, &y);
	for(int i=1; i<=n; ++i)
		scanf("%d %d", &a[i], &b[i]);
	dp[0][0][0]=0;
	//第i个菜肴
	for(int i=1; i<=n; ++i) {
		//一共吃了j道菜肴
		for(int j=0; j<i; ++j) {
			//甜度是多少
			for(int ta=0; ta<=x; ++ta) {
				dp[i][j][ta]=min(dp[i][j][ta],dp[i-1][j][ta]);
				if(ta+a[i]<=x) {
					dp[i][j+1][ta+a[i]]=min(dp[i][j+1][ta+a[i]],dp[i-1][j][ta]+b[i]);
				}

			}
		}
	}
	for(int i=n; i>=0; --i) {
		for(int j=x; j>=0; --j) {
			if(dp[n][i][j]<=y) {
				printf("%d",min(i+1,n));
				return 0;
			}
		}

	}
	return 0;
}
  • 15
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值