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;
}