算法竞赛入门【码蹄集进阶塔335题】(MT2201-2225)
文章目录
- 算法竞赛入门【码蹄集进阶塔335题】(MT2201-2225)
- 前言
- 为什么突然想学算法了?
- 为什么选择码蹄集作为刷题软件?
- 目录
- 1. MT2101 竹鼠发瓜子
- 2. MT2102 竹鼠发瓜子(二)
- 3. MT2103 水温调节
- 4. MT2104 活动安排
- 5. MT2105 甜品供应
- 6. MT2106 斐波那契数列的组合
- 7. MT2107 配对
- 8. MT2108 数列分段
- 9. MT2109 拼数字
- 10. MT2110 删除合并01串
- 11. MT2111 小码哥爱数字
- 12. MT2112 子串分割
- 13. MT2113 泼墨淋漓
- 14. MT2114 很重的枪
- 15. MT2215 小船过河
- 16. MT2116 桌子腿
- 17. MT2117 最大值
- 18. MT2118 最小串
- 19. MT2219 三国杀
- 20. MT2120 摘果子
- 21. MT2121 能量供应
- 22. MT2122 小码哥的跳棋游戏新编
- 23. MT2123 小码哥的布阵指挥
- 24. MT2124 活动分组
- 25. MT2125 外卖递送
- 结语
前言
为什么突然想学算法了?
> 用较为“官方”的语言讲,是因为算法对计算机科学的所有分支都非常重要。 在绝大多数的计算机科学分支领域中,要想完成任何实质性的工作,理解算法的基础知识并掌握与算法密切相关的数据结构知识是必不可少的。
> 但从实际而言,是因为当下快到了考研和找工作的年纪(ಥ_ಥ),无论走哪一条路,都不免需要一些相对丰富的算法知识,是故,便产生了一个暑假速成算法的计划,可能对于像我这种算法竞赛小白而言,几乎很难,但我仍然还是想尝试一下,毕竟,梦想还是要有的,万一实现了呢?~( ̄▽ ̄~)~
为什么选择码蹄集作为刷题软件?
码蹄集,是在全国高等学校计算机教学与产业实践资源建设专家委员会(TIPCC) 指导下建设的,其依托全国各大名校计算机系和清华大学出版社等单位的强大资源,旨在为计算机学习爱好者提供全面和权威的计算机习题。
目录
1. MT2101 竹鼠发瓜子
(1)题目描述
今天小码哥兄弟俩打算给竹鼠们发瓜子吃。竹鼠们排成一列纵队,兄弟俩会根据竹鼠的体重来评定发瓜子的数量。
其中第i个竹鼠的体重是a,发瓜子时需要满足以下要求:
1.相邻竹鼠中体重高的竹鼠的瓜子必须更多,若它们体重相等,则它们分到的瓜子数量必须相等。
2.每只竹鼠至少有一个瓜子。
3.因为经费有限,在满足条件1和2的前提下,发出尽可能少的瓜子。
格式
输入格式:
第一行是一个整数n,表示竹鼠的个数。
第二行有n个整数,表示竹鼠们的体重。
.
输出格式: 最少要准备的瓜子数量。
样例1
输入:
5
3 4 5 4 3
.
输出: 9
(2)参考代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
typedef long long ll;
int ai[N],ao[N];
ll res[N];
int main()
{
int n,min_pt,sum=0;
cin >> n;
for (long i = 0; i < n; i++)
{
cin >> ai[i];
if (ai[i] < ai[min_pt])
min_pt = i;
}
ao[min_pt] = 1;
for (long i = min_pt; i < n - 1; i++)
if (ai[i] < ai[i + 1]) ao[i + 1] = ao[i] + 1;
else if (ai[i] > ai[i + 1])
{
ao[i + 1] = 1;
if (ao[i] <= 1)
{
long nt = i;
do {
ao[nt]++;
nt--;
if (((ao[nt] > ao[nt + 1]) && (ai[nt] > ai[nt + 1])) ||((ao[nt] == ao[nt + 1]) && (ai[nt] == ai[nt + 1])))
break;
} while (ai[nt] >= ai[nt + 1]);
}
}
else ao[i + 1] = ao[i];
for (long i = min_pt; i > 0; i--)
if (ai[i] < ai[i - 1]) ao[i - 1] = ao[i] + 1;
else if (ai[i] > ai[i - 1])
{
ao[i - 1] = 1;
if (ao[i] <= 1) {
long nt = i;
do{
ao[nt]++;
nt++;
if (((ao[nt] > ao[nt - 1]) && (ai[nt] > ai[nt - 1])) ||((ao[nt] == ao[nt - 1]) && (ai[nt] == ai[nt - 1])))
break;
} while (ai[nt] >= ai[nt - 1]);
}
}
else ao[i - 1] = ao[i];
for (long i = 0; i < n; i++) sum += ao[i];
cout << sum << endl;
}
2. MT2102 竹鼠发瓜子(二)
(1)题目描述
小码哥一口气买了一包有m颗瓜子的qiaqia瓜子,现在要把这些瓜子分给n只竹鼠。
每只竹鼠都有一个瓜子的期望值,如果没有达到竹鼠的期望值,竹鼠就会生气,竹鼠生气的程度等于它少得到瓜子数的平方。
小码哥要合理分配糖果,使竹鼠们生气的程度之和最小。
格式
输入格式:
第一行两个整数m和n。
第二行n个整数,表示竹鼠的期望值。
.
输出格式: 输出一个整数,竹鼠们的最小生气总数。
样例1
输入:
10 4
4 5 2 3
.
输出: 4
(2)参考代码
import time
from typing import List, Tuple
from collections import deque, Counter
from queue import PriorityQueue
import math
from functools import lru_cache
import random
import copy
import sys
sys.setrecursionlimit(99999999)
def main():
a, b = map(int, input().split())
arr = list(map(int, input().split()))
arr.sort()
tot = sum(arr)
tot -= a
cnt = b
ans = []
for v in arr:
avg = tot // cnt
x = tot % cnt
if v <= avg:
ans.append(v)
tot -= v
else:
if x > 0:
ans.append(avg + 1)
tot -= (avg + 1)
else:
ans.append(avg)
tot -= avg
cnt -= 1
val = 0
for v in ans:
val += v ** 2
print(val)
if __name__ == '__main__':
main();
3. MT2103 水温调节
(1)题目描述
小码哥家里的浴缸里有一个冷水龙头和一个热水龙头,流出的水温分别是t和tz;它们的流速最大是每秒a和a2个单位,并且可以调节到О至最大流速间的任意一个整数速度。假如两个龙头的流速分别是y和y2( 0≤g1,32 ≤10),那么最终的水温就是
现在,你要安排两个水龙头的流速,满足:
1.最终水温不低于t0 ;
2.在满足上一条的前提下,水温尽可能接近t0;
3.在满足上一条的前提下,总流速尽可能大。
格式
输入格式: 一行5个整数t1, t2,C1, 02, to ; 1<t1≤to≤t2≤10R; 1≤Z1 , 2 ≤106
.
输出格式: 输出一行两个数表示y1和 y2
样例1
输入: 10 70 100 100 25
.
输出: 99 33
备注:
一定要仔细注意数据范围。
(2)参考代码
#include<bits/stdc++.h>
using namespace std;
int t1,t2,x1,x2,t0;
double best=0x3f3f3f3f,t;
pair<int,int> ans;
int main()
{
cin>>t1>>t2>>x1>>x2>>t0;
double y1=x1,y2=x2;
while(y1>=0&&y2>=0)//坑点一:这里是“>=”而不是“>”
{
t=double(t1*y1+t2*y2)/(y1+y2);//坑点二:别忘了转double
if(t<t0) {y1--; continue;}//先考虑温度高的情况
if(t-t0<best) best=t-t0,ans.first=y1,ans.second=y2;
//坑点三:这里如果写成if(t<best)best=t的话会在精度上出问题
y2--;//最后考虑温度低的情况
}
cout<<ans.first<<' '<<ans.second<<endl;
return 0;
}
4. MT2104 活动安排
(1)题目描述
小码哥又被安排去处理公司的大厅了,公司只有一个大厅,但有许多的活动需要召开,所以需要提前预约,该公司有n 个部门,每个部门会举办一个活动。每个部门会向小码哥上报他们预期使用大厅的时间区间 (a,b)(开区间),小码哥会根据这些情报做安排,一个时间只能有一个活动在召开,活动一旦开始必须结束才能进行下一个活动。
小码哥想要尽可能多的满足活动的召开,于是请你来安排活动。
格式
输入格式:
第一行一个整数n,代表部门的个数。
接下来的n行,每行两个整数a,b,代表时间区间。
.
输出格式: 输出一个整数代表能够召开的最大活动个数。
样例1:
输入:
4
1 3
4 6
2 5
1 7
.
输出:2
(2)参考代码
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
PII t[500010];
bool cmp(PII a,PII b){
return a.second<b.second;
}
int main( )
{
int n;
cin >> n;
int l,r;
for(int i=1;i<=n;i++){
scanf("%d%d",&l,&r);
t[i] = make_pair(l,r);
}
sort(t+1,t+1+n,cmp);
r = t[1].second;
int ans = 1;
for(int i=2;i<=n;i++){
if(r<=t[i].first){
r = t[i].second;
ans++;
}
}
cout << ans <<endl;
return 0;
}
5. MT2105 甜品供应
(1)题目描述
、
格式
输入格式:
第1行:两个空格分隔的整数:C和L
第2…C+1行:每行用两个整数描述手下i 的甜度要求:l和Ti
第C+2.….C+L+1行:每行两个整数描述某一种甜品的甜度和个数:u;和numi
.
输出格式: 带有整数的单行,表示可以使最多的手下吃到他们喜欢的甜品的人数。
样例1
输入:
3 2
3 10
2 5
1 5
6 2
4 1
.
输出: 2
(2)参考代码
#include <bits/stdc++.h>
using namespace std;
string a[105];
int n,m;
int b[100005],c[105],d[105];
int main()
{
cin>>n>>m;
int i,j,t,x=0,y=0;
for(i=0;i<n;i++) cin>>b[i];
sort(b,b+n);
for(i=0;i<m;i++) cin>>a[i];
for(i=0;i<m;i++) c[i]=1;
for(i=0;i<m&&(c[i]!=-1);i++)
{
for(j=i+1;j<m;j++)
{
if(a[j]==a[i])
{
c[i]++;
c[j]=-1;
}
}
}
j=0;
for(i=0;i<m;i++)
{
if(c[i]>0)
{
d[j]=c[i];
j++;
}
}
sort(d,d+j);
for(i=0,t=j-1;t>=0;i++,t--) x+=d[t]*b[i];
for(i=n-1,t=j-1;t>=0;i--,t--) y+=d[t]*b[i];
cout<<x<<" "<<y;
return 0;
}
6. MT2106 斐波那契数列的组合
(1)题目描述
给定一个正整数n,请写一个函数 MinFibonacciNumbers,返回和为n的斐波那契数字的最少数目。
斐波那契数列:
F1=1
F2=1
Fn= Fn-1+Fn-2,n > 2
保证一定存在解。
格式
输入格式: 第一行输入正整数n
.
输出格式: 输出满足要求的数字的最少数目。
样例1
输入: 19
.
输出: 3
(2)参考代码
arr = [1,1]
while arr[-1] < 10**9:
arr.append(arr[-1]+arr[-2])
def main():
#code here
tot = int(input())
cnt = 0
for v in arr[::-1]:
while v <= tot:
tot -= v
cnt += 1
if tot == 0:
break
print(cnt)
if __name__ == '__main__':
main();
7. MT2107 配对
(1)题目描述
小码哥有m个手下,他知道这m个人的居住地址,用a[i]表示,他现在想集合k个小组,每个小组中有两人,如果某两个人成为了一组,他们需要移动到一个相同的地方,即相同的坐标下。
然而这些手下移动需要花费经费,花费的钱与移动距离成正比,即如果手下A(a[]==2)和手下B(a[i]==9)想要结成小队,需要9-2=7的经费;
小码哥希望经费的总量最小;
一个手下不能同时加入两个小组;
数据保证α[i]按严格升序排列。
格式
输入格式:
第一行输入正整数m, k
第二行输入m个整数空格分开
.
输出格式: 输出最小的经费
样例1
输入:
5 2
1 3 4 6 12
.
输出: 4
备注:
提示1≤a[i]≤1e6 ,1 ≤k * 2≤m≤ 1e4
(2)参考代码
#include<bits/stdc++.h>
using namespace std;
long long spents=0,min_s=10000000000;
void backtracking(vector<int>& vec, int start_index,int k,int flag){
if(k==0){
// cout‹‹spents‹‹"ss \n";
if(spents<min_s){
min_s=spents;
// cout‹‹min_s‹<"min_s\n";
if(min_s<=flag){
// cout<‹"clear \n".
vec. clear();
}
}
return;
}
if (spents<min_s){
for(int i=start_index;i<vec.size();++i){
if (vec[i]>min_s) continue;
spents+=vec[i];
backtracking(vec, i+2,k-1, flag);
spents-=vec[i];}
}
return;}
int main( )
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, num, pre, k, flag=0, count_k=0;
vector<int> dest_len;
multiset<int> dest_set;
cin>>n>>k;
cin>>pre;
for(int i=1;i<n;++i){
cin>>num;
dest_len.push_back(num-pre);
dest_set.emplace(num-pre);
pre=num;}
vector<int> t=dest_len;
for(auto c:dest_set){
flag+=c;
++count_k;
if(count_k==k) break;
}
backtracking(dest_len,0,k,flag);
cout<<min_s;
return 0;
}
8. MT2108 数列分段
(1)题目描述
对于给定的一个长度为Ⅳ的正整数数列Ai,现要将其分成连续的若干段,并且每段和不超过M(可以等于M ),问最少能将其分成多少段使得满足要求。
格式
输入格式:
第1行包含两个正整数N, M ,表示了数列A的长度与每段和的最大值,第2行包含NⅣ个空格隔开的正整数A,如题目所述。
.
输出格式: 一个正整数,输出最少划分的段数。
样例1
输入:
5 6
4 2 4 5 1
.
输出: 3
(2)参考代码
def main():
#code here
N,M=map(int, input () .split ())
num=list(map(int, input () .split ()))
s=0
res=1
for i in range(N):
if s+num[i]<=M:
s+=num[i]
else:
res+=1
s=num[i]
print(res)
pass
if __name__ == '__main__':
main();
9. MT2109 拼数字
(1)题目描述
数学世家的小码哥儿时就对数学深感兴趣,喜欢上了回文数的对称之美
因此小码哥随便从报纸上裁下来一段纸条,将纸条按照单位长度裁成了一格格的格子,其中每个格子里要么是一个一位数数字要么是一块空白,小码哥可以在空白格子上再写上随便一个一位数数字,然后将这些格子(包括用空白格子写的数字格子))排列成一个回文数(不要求用完所有格子)
小码哥想问你他可以拼出的最大的回文数是多少
格式
输入格式: 第一行一个字符串,表示小码哥裁下来的纸条,保证字符串只由数字和空格组成
.
输出格式: 一个整数,表示小码哥能拼出的最大的回文数(不考虑前导零)
样例1
输入: 11 22 33
.
输出: 93211239
备注:
其中:1≤字符串长度≤10的6次
(2)参考代码
#include <bits/stdc++.h>
#define int long long
#define end END
#define endl "\n"
#define debug(x) cout << "*----" << x << "----*" << endl
#define show(x) cout << #x "=" << x << endl
using namespace std;
typedef long long ll;
const int N = 3e4 + 7;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + 7;
string s[N];
int trie[N * 10][26];
int tot;
bool end[N*10];
void insert(int x)
{
int p = 0;
for (int i = 0; i <s[x].size(); i++) {
int ch = s[x][i] - 'a';
if (trie[p][ch] == 0)
trie[p][ch] = ++tot;
p = trie[p][ch];
}
end[p] = 1;
}
int g[26][26];
bool ask(int x)
{
memset(g, 0, sizeof g);
int p = 0;
for (int i = 0; i < s[x].size(); i++) {
int ch = s[x][i] - 'a';
if (end[p])
return false;
for (int j = 0; j < 26; j++) {
if (j != ch && trie[p][j]) {
if(g[ch][j]>0) return false;
}
}
for(int j=0;j<26;j++){
if (j != ch && trie[p][j]) {
g[ch][j] = -1;
g[j][ch] = 1;
vector<int> a, b;
for (int k = 0; k < 26; k++) {
if (g[j][k] == -1) {
g[ch][k] = -1;
g[k][ch] = 1;
b.push_back(k);
}
if (g[ch][k] == 1) {
g[j][k] = 1;
g[k][j] = -1;
a.push_back(k);
}
}
for (auto u : a) {
for (auto v : b) {
g[u][v] = -1;
g[v][u] = 1;
}
}
}
}
p = trie[p][ch];
}
return true;
}
signed main()
{
int n;
cin >> n;
int sum=0;
for (int i = 1; i <= n; i++) {
cin >> s[i];
sum+=s[i].size();
insert(i);
}
if(sum>3e5) while(true);
if(n>3e4) while(true);
if(tot>3e5) while(true);
vector<int> ans;
for (int i = 1; i <= n; i++) {
if (ask(i))
ans.push_back(i);
}
cout << ans.size() << endl;
for (auto u : ans) {
cout << s[u] << endl;
}
return 0;
}
10. MT2110 删除合并01串
(1)题目描述
给一个全是01的字符串,每次只能删除字符相同的子串(全0或全1),然后合并字符串:删除子串后,剩余字符串按原顺序排列合并。
每次删除会得到一定分数,它的值为Ⅰ(删去字符串的长度)*a+b。
求可以得到的最大分数。
格式
输入格式:
第一行一个整数t表示测试组数
每一组数据第一行包括n, a,b (1<n ≤100;—100 ≤ a,b ≤100),其中n是字符串长度, a、b为计算公式两个参数第二行输入01串
.
输出格式: 每一组数据输出一个结果
样例1
输入:
3
3 2 0
000
5 -2 5
11001
6 1 -4
100111
.
输出:
6
15
-2
(2)参考代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5e6 + 100;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const unsigned long long P = 13131;
char s[N];
int ans[N];
unsigned long long p[N];
unsigned long long pre[N];
unsigned long long suf[N];
int long long hash1(int l, int r)
{
if (l <= r)
return pre[r] - pre[l - 1] * p[r - l + 1];
else
return suf[r] - suf[l + 1] * p[l - r + 1];
}
int main(){
scanf("%s", s + 1);
int n = strlen(s + 1);
p[0] = 1;
for (int i = 1; i <= n; i++)
p[i] = p[i - 1] * P;
for (int i = 1; i <= n; i++) {
pre[i] = pre[i - 1] * P + s[i];
}
for (int i = 1; i <= n; i++) {
int x = n - i + 1;
suf[x] = suf[x + 1] * P + s[x];
}
for (int i = 1; i <= n; i++) {
if (hash1(1, i) == hash1(i, 1))
ans[i] = ans[i / 2] + 1;
}
int sum = 0;
for (int i = 1; i <= n; i++) {
printf("%d%c",ans[i]," \n"[i==n]);
}
return 0;
}
11. MT2111 小码哥爱数字
(1)题目描述
小码哥很喜欢数字,于是一天他找到老师给他出一道有关数字的题目。老师给他一个位数很多的正整数N(不超过250位),让小码哥去掉其中任意k个数字后剩下的数字按原左右次序将组成一个新的非负整数。小码哥觉得老师在刁难他(因为小码哥才一年级),所以他找身为程序员的你编程对给定的N和k,寻找—种方案使得剩下的数字组成的新数最小。
格式
输入格式:
n(高精度的正整数)。不必考虑前导0。
k(需要删除的数字个数)。保证有输出,,即k小于n的位数。
.
输出格式: 最后剩下的最小数。
样例1
输入:
175438
4
.
输出: 13
(2)参考代码
from collections import deque,Counter
from queue import PriorityQueue
def main():
#code here
s = str(int(input()))
k = int(input())
n = len(s)
bits = n-k;
que = PriorityQueue()
for i in range(n-bits):
que.put((s[i],i))
cur_pos = -1
ans = []
all_zero = True
pos = None
for i in range(n-bits,n):
que.put((s[i],i))
while que.queue[0][1] <= cur_pos:
que.get()
v,idx = que.get()
ans.append(v)
if v!= '0':
if all_zero:
pos = len(ans)-1
all_zero = False
cur_pos = idx
if all_zero:
print(0)
return
print(''.join(ans[pos:]))
if __name__ == '__main__':
main();
12. MT2112 子串分割
(1)题目描述
小码哥有一个字符串s和一个单词u,他想把s分为若干个子串满足每个子串均有一个子序列为w,问最多可以分出多少个子串。
格式
输入格式: 输入共两行,第一行一个仅包含小写字母的字符串s,第二行一个字符串w,满足1≤wl≤ls≤1x 105。
.
输出格式: 输出一个整数表示最多能分出的子串数量。
样例1
输入:
aaabbbcccabc
abc
.
输出: 2
备注:
可以分为aaabbbc和 ccabc两个子串,它们均含有子序列abc
(2)参考代码
#include<bits/stdc++.h>
#include<iostream>
#include<string>
using namespace std;
int main( )
{
string s,w;
cin>>s;
cin>>w;
int len_w=w.length();
int len_s=s.length();
int i=0,j=0,ans=0;
while(i!=len_s)
{
if(s[i]==w[j]){
j++;
if(j==len_w)
{
ans++;
j=0;
}
}
i++;
}
cout<<ans<<endl;
return 0;
}
13. MT2113 泼墨淋漓
(1)题目描述
小码哥有n幅画,每幅画都有一个编号ai,编号为非负数且可以相同。他想改变一些画的编号,使得n幅画使用的不同编号数量不超过k,问最少需要改变多少幅画的编号?
数据范围1≤k≤n ≤200000。
格式
输入格式: 第一行输入n, k,第二行输入i 。
.
输出格式: 输出需要改变编号的画的最少数量。
样例1
输入:
5 2
1 1 2 2 5
.
输出: 1
(2)参考代码
#include<bits/stdc++.h>
using namespace std;
int n,k;
map<int,int> cnt;
vector<int> a;
int main( )
{
cin>>n>>k;
for (int i=1,x;i<=n;++i){
cin>>x;
if(cnt.count (x))++cnt[x];
else cnt [x]=1;
}
for (map<int,int>::iterator it=cnt.begin();it!=cnt.end();++it)a.emplace_back(it->second);
sort (a.begin(),a.end ());
if(a.size()<=k){cout<<0<<endl;return 0;}
int sum(0);
for (int i=1;i<=k;++i)
sum+=a[a.size()-i];
cout<<n-sum<<endl;
return 0;
}
14. MT2114 很重的枪
(1)题目描述
刻俄柏在打怪,她的面前有n个怪,第 i个怪的血量为ai。对于一个怪物,她有两种攻击方式:
1.使用ai次普通攻击消灭该怪物
⒉.施放一次技能消灭该怪物
刻俄柏最多只能使用k次技能,她想知道最少使用多少次普通攻击消灭所有怪物。
格式
输入格式:
第一行输入n,k,第二行输入ai。数据范围0≤ai,k ≤1091≤n ≤2×105。。
.
输出格式: 输出使用普通攻击的最少次数。
样例1
输入:
3 1
4 1 5
.
输出: 5
(2)参考代码
#include<bits/stdc++.h>
using namespace std;
int a[200010];
int main( )
{
int i,j,k,m,n;
long long sum=0;
scanf("%d%d",&n,&k);
for(i=1;i<=n;i++){
scanf("%d",&a[i]);
}
sort(a+1,a+n+1);
for(i=1;i<=n-k;i++){
sum+=a[i];
}
printf("%lld\n",sum);
return 0;
}
15. MT2215 小船过河
(1)题目描述
n个人想要过河,但现在只有一条小船(有船夫)。小船每次最多载两人过河(不考虑船夫),且有最大承载重量t。请计算小船最少要来回几次。
格式
输入格式:
第一行输入2个整数n和t(0<n ≤105,0<t<232 )
第二行输入n个整数代表每个人的体重(范围:[1,232-1],均小于t)
.
输出格式: 输出一个整数
样例1
输入:
4 3
3 2 2 1
.
输出: 3
(2)参考代码
#include <bits/stdc++.h>
using namespace std;
int main() {
cin.tie(NULL)->ios_base::sync_with_stdio(false);
int n, m;
cin >> n >> m;
vector<int> v(n);
for (auto &x : v)
cin >> x;
multiset<int> s;
for (auto x : v)
s.insert(x);
int ans = 0;
while (!s.empty()) {
auto x = *s.rbegin();
s.erase(s.find(x));
if (!s.empty() && s.upper_bound(m - x) != s.begin())
s.erase(prev(s.upper_bound(m - x)));
++ans;
}
cout << ans << endl;
return 0;
}
16. MT2116 桌子腿
(1)题目描述
有一张桌子,有n个腿。第i根腿的长度是l。
现在要拿掉一些腿,使得桌子稳定,拿掉第i根腿需要d。的能量。
稳定的条件是,假如拿掉若干条腿之后,桌子还有k个腿,那么长度最长的腿的数目要超过一半。比如桌子有5根腿,那么至少要有3根腿是最长的。另外,只有一根腿的桌子是稳定的,两个腿的桌子想要稳定,必需长度是一样的。
你的任务是拿掉若干腿,使得桌子稳定,并且所消耗的能量要最少。
格式
输入格式: 第一行一个数n,第二行为l,第三行为d;1-4。
.
输出格式: 一个数,表示需要消耗的最少能量
样例1
输入:
2
1 5
3 2
.
输出: 2
备注:
1<=n<=1e5, 1<= li<=1e5,1<= d;<=200
(2)参考代码
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <string.h>
#include <algorithm>
#include <map>
#include <vector>
#include <algorithm>
#include <vector>
#include <set>
#include <queue>
#include <string>
#include <unordered_map>
#include <unordered_set>
using namespace std;
#define MAX_SPLAY_NODE_NUM 100005
struct Node {
int child[2]; // 左右子节点编号
int parent; // 父节点编号
int val; // 节点数值
long long sum; // 子树和
int size; // 子树大小
int dep; // 子树深度
int flag; // 懒标记i
void init(int p, int v) {
child[0] = child[1] = 0;
parent = p;
val = v;
sum = v;
size = 1;
dep = 1;
flag = 0;
}
} __splay_node_pool[MAX_SPLAY_NODE_NUM];
int __splay_pool_pos = 1;
class SplayTree {
public:
int __root;
Node* __pool;
int& __pool_pos;
class __init_guard {
public:
__init_guard() {
__splay_node_pool[0].init(0, 0);
__splay_node_pool[0].size = 0;
__splay_node_pool[0].sum = 0;
}
};
static __init_guard __guard;
public:
SplayTree() : __pool_pos(__splay_pool_pos), __root(0),
__pool(__splay_node_pool) { }
Node& get_node(int idx) { return __pool[idx]; }
int get_root() { return __root; }
void move(int x, int k) { __move(x, k); }
int insert(int val) {
int u = __root, p = 0;
while (u != 0) {
p = u;
__push_down(p);
u = __pool[u].child[val > __pool[u].val];
}
u = __pool_pos++;
if (__pool_pos > MAX_SPLAY_NODE_NUM) { printf("overflow!!!\n");
return -1; }
if (p != 0) { __pool[p].child[val > __pool[p].val] = u; }
__pool[u].init(p, val);
while (p) { __push_up(p); p = __pool[p].parent; }
__move(u, 0);
return u; }
int get_kth_idx(int k) {
int u = __root;
while (u) {
__push_down(u);
if (__pool[__pool[u].child[0]].size >= k) { u =__pool[u].child[0]; }
else if (__pool[__pool[u].child[0]].size + 1 == k) { return u; }
else { k -= __pool[__pool[u].child[0]].size + 1; u =__pool[u].child[1]; }
}
return 0;
}
long long get_sum(int idx) { return __pool[idx].sum; }
void reverse_flag(int idx) {
__push_down_rev(idx, 1);
}
private:
void __push_up(int idx) {
Node& node = __pool[idx];
node.size = 1 + __pool[node.child[0]].size +__pool[node.child[1]].size;
node.sum = node.val + __pool[node.child[0]].sum + __pool[node.child[1]].sum;
node.dep = 1 + max(__pool[node.child[0]].dep,__pool[node.child[1]].dep);
}
void __push_down_rev(int idx, int f) {
if (!idx) { return; }
if (!f) { return; }
__pool[idx].flag ^= 1;
swap(__pool[idx].child[0], __pool[idx].child[1]);
}
void __push_down(int idx) {
if (__pool[idx].flag) {
// 下传区间翻转标记
__push_down_rev(__pool[idx].child[0], __pool[idx].flag);
__push_down_rev(__pool[idx].child[1], __pool[idx].flag);
__pool[idx].flag = 0;
}
}
void __rotate(int x) {
int y = __pool[x].parent, z = __pool[y].parent;
__push_down(y); __push_down(x);
int k1 = __pool[y].child[1] == x, k2 = __pool[z].child[1] == y;
__pool[z].child[k2] = x;
__pool[x].parent = z;
__pool[y].child[k1] = __pool[x].child[k1^1];
__pool[__pool[x].child[k1^1]].parent = y;
__pool[x].child[k1^1] = y;
__pool[y].parent = x;
__push_up(y); __push_up(x);
}
void __move(int x, int k) {
while (__pool[x].parent != k) {
int y = __pool[x].parent, z = __pool[y].parent;
if (z != k) {
if ( (__pool[y].child[1] == x) != (__pool[z].child[1]== y) ) { __rotate(x); }
else { __rotate(y); }
}
__rotate(x);
}
if (k == 0) { __root = x; }
}
};
SplayTree::__init_guard SplayTree::__guard;
struct node {
int len, cost;
bool operator < (const node& o) const {
return len < o.len;
}
};
const int N = 100005;
int l[N], d[N];
int main(){
#ifdef TEST
freopen("/Users/grh/Programming/CLionProjects/AlgoCoding/input.txt","r", stdin);
#endif
int n; scanf("%d", &n);
int len, cost;
vector<node> v;
long long tot = 0;
for (int i = 0; i < n; i++) scanf("%d", l+i);
for (int i = 0; i < n; i++) scanf("%d", d+i);
for (int i = 0; i < n; i++) {
v.push_back({l[i], d[i]});
tot += d[i];
}
sort(v.begin(), v.end());
SplayTree tree; tree.insert(0); // 左边加一个哨兵
int ii = 0;
long long mx = 0;
while (ii < n) {
int jj = ii;
while (jj+1 < n && v[jj+1].len == v[ii].len) {
jj++;
}
int k = jj-ii + 1;
k = min(ii, k-1);
long long s;
if (k > 0) {
int idx1 = tree.get_kth_idx(ii + 1 - k);
int idx2 = tree.get_kth_idx(ii + 1 - k + 1);
tree.move(idx1, 0);
tree.move(idx2, idx1);
s = tree.get_sum(idx2);
} else {
s = 0;
}
for (int i = ii; i <= jj; i++) {
tree.insert(v[i].cost); s += v[i].cost;
}
mx = max(mx, s);
ii = jj+1;
}
printf("%lld\n", tot - mx);
return 0;
}
17. MT2117 最大值
(1)题目描述
给定一个长度为n的整数s 和一个整数a , s中的每一位数字都∈[1,9],且整数s可能是负数。现在将插入一个整数α到s的某一个位置,使得插入之后的s最大。(不可以插到负号左边)
请编写一个函数MAXVALUE,输入参数s字符串和插入的值。,完成上述的功能,使得插入a之后的s最大。
格式
输入格式:
第一行两个整数n, z(1 <=n<= 105,1<=C<=9)
第二行一个字符串s表示长度为n的整数
.
输出格式: 输出一个长度为n+1的字符串s
样例1
输入:
3 2
-13
.
输出: -123
(2)参考代码
#include<bits/stdc++.h>
std::string MAXVALUE(std::string s, int x)
{
bool sg = s[0] == '-';
bool flag = true;
for (auto it = s.begin(); it != s.end(); it++) {
if (flag) {
if (sg && it!= s.begin() && *it > x + '0' || !sg && *it < x
+ '0') {
s.insert(it, x + '0');
flag = false;
}
}
}
if (flag) {
s.push_back(x + '0');
}
return s;
}
int main( )
{
std::ios::sync_with_stdio(false);
int n, x;
std::cin >> n >> x;
std::string s;
std::cin >> s;
std::cout << MAXVALUE(s, x) << std::endl;
return 0;
}
18. MT2118 最小串
(1)题目描述
给定一个由‘0’ , '1’, ‘2’组成的字符串S。可以交换相邻′O’,‘1′或’1’, ‘2’的位置(例如:′12’- ‘21’ ; ‘01’- '10’)请输出原字符串经过任意转换后字典序最小的字符串。原字符串长度不超过105。
格式
输入格式: 字符串s
.
输出格式: 转化后字典序最小的字符串
样例1
输入: 100210
.
输出: 001120
备注:
1≤ \S|≤100000
(2)参考代码
#include <bits/stdc++.h>
using namespace std;
void solve() {
string str, res; cin >> str;
int n = str.length(), one = 0;
for (int i = 0; i < n; ++i) one += str[i] == '1';
for (int i = 0; i < n; ++i)
if (str[i] == '2') {
res += string(one, '1');
for (int j = 0; j < n; ++j)
if (str[j] != '1')
res += str[j];
cout << res << "\n";
return;
} else if (str[i] == '0') {
int zero = 0, p = i;
while (p < n && str[p] != '2') zero += str[p++] == '0';
res += string(zero, '0');
res += string(one, '1');
for (int j = p; j < n; ++j) if (str[j] != '1') res +=str[j];
cout << res << "\n";
return;
}
cout << str << "\n";
}
int main() {
ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
solve();
return 0;
}
19. MT2219 三国杀
(1)题目描述
小码哥和小b酷爱三国杀。在一局游戏中,每人有n名武将,每名武将有一个武力值。每一回合,每人挑选一名武将作战,武力值大的获胜,同时该武将进入弃牌堆。如果提前知道了小b的出牌顺序,小码哥能最多获胜几回合
格式
输入格式:
第1行包含一个正整数n
第2行n个正整数a ,表示小码哥的武将
第3行n个正整数b ,表示小b的武将
.
输出格式: 一个整数,输出小码哥最多获胜的回合数
样例1
输入:
3
1 2 3
1 2 3
.
输出: 2
(2)参考代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int i,j,k,n,m,t,a[1000005];
multiset<int> s;
int main() {
cin>>n;
for(i=1;i<=n;i++){
cin>>k;
s.insert(k);
}
for(i=1;i<=n;i++) cin>>a[i];
sort(a+1,a+n+1);
for(i=n;i>=1;i--){
auto it = s.upper_bound(a[i]);
if(it!=s.end()){
s.erase(it);
t++;
}
}
cout<<t;
return 0;
}
20. MT2120 摘果子
(1)题目描述
小码哥今天被奖励了许多果子,但是这些果子需要他自己去摘,小码哥摘一次果子需要1分钟。
在一个果园中,有n种果树,他们分布在一条数轴上,有独立的坐标a,小码哥在第一颗果树下开始行走,从i棵走到i+1棵需要 t,的时间
如果小码哥选择在这棵果树下摘,那么第一个1分钟将会摘下a;的果子,第二次摘将摘下a;~ d;个果子。以此类推。
但是小码哥的时间有限,只有t的总时间来摘果子
请你帮助他算出,可以摘下的最大果子数是多少。
格式
输入格式:
第一行输入正整数n,t ;
第二行n个数,表示第一次可以摘的果子数ai。
第三行n个数,表示每次摘减少的可摘数d。
第四行n-1个数,表示数之间的间隔行走时间ti。
.
输出格式: 输出一个数,最大的果子数
样例1
输入:
2 12
10 6
2 5
2
.
输出: 37
备注:
提示:所有变量都为正整数
1<t ≤192,1 ≤n≤25,1 ≤ai , d,ti≤ 1e5
(2)参考代码
#include<bits/stdc++.h>
using namespace std;
int n,m,ct;
long long ans;
long long f[30][200005];
int a[30],d[30],t[30];
priority_queue<int>q;
void solve(int w){
int sum=m-t[w];
long long rans=0;
if(sum<0) return ;
for(int i=1;i<=w;i++){
int ct=a[i];
for(int j=1;j<=sum;j++){
q.push(ct);
ct-=d[i];
if(ct<0) break;
}
}
while(sum>0 && !q.empty()){
rans+=q.top();
q.pop();
sum--;
}
ans=max(ans,rans);
while(!q.empty()) q.pop();
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) cin>>d[i];
for(int i=2;i<=n;i++) cin>>t[i];
for(int i=3;i<=n;i++) t[i]+=t[i-1];
for(int i=1;i<=n;i++) solve(i);
cout<<ans;
return 0;
}
21. MT2121 能量供应
(1)题目描述
最近小码哥迷上了一款建造管理类游戏,游戏中的设施需要能量塔提供能量,设施运行时需要一定数量的能量塔在附近才能运作,不同的设施需要的能量塔的数量以及能链接到能量塔的范围也不一样,超出设施链接范围的能量塔无法为设施提供能量,不同设施可以重复利用同一座能量塔。
现在小码哥修建了一条长为n的道路来放置能量塔,但能量塔的修建成本稍高,小码哥不想花太多时间收集资源建造过多能量塔,现在小码哥用一个区间[s,e]告诉你每个设施链接到能量塔的有效范围,以及每个设施需要的能量塔数量t,想要你告诉他最少修建多少个能量塔即可让所有设施运作
格式
输入格式:
两个正整数n, b,表示道路的长度和设施的数量
接下来b行每行三个正整数s,e, t,表示设施的链接范围的起始和结尾以及需要的能量塔数量
.
输出格式: 一个整数,表示需要的最少的能量塔的数量
样例1
输入:
8 3
1 3 1
4 5 1
3 3 1
.
输出: 2
备注:
1≤n≤105, 1≤b<104。
1≤sj≤ej≤n,1≤tj≤ei-Sj+1。
(2)参考代码
#include<bits/stdc++.h>
using namespace std;
typedef long long int LL;
const int maxn = 100005;
int n, b;
vector<int> se[maxn];
vector<int> st[maxn];
int ans[maxn], len[maxn];
int main() {
cin >> n >> b;
for(int i = 1; i <= b; i++) {
int s, e, t;
cin >> s >> e >> t;
se[s - 1].push_back(e);
st[s - 1].push_back(t);
len[s - 1]++;
}
for(int i = 0; i <= n; i++) {
if(i != 0) ans[i] = max(ans[i - 1], ans[i]);
for(int j = 0; j < len[i]; j++) {
ans[se[i][j]] = max(ans[se[i][j]], ans[i] + st[i][j]);
int tt = ans[se[i][j]] - se[i][j];
for(int k = i + 1; k < se[i][j]; k++)
ans[k] = max(ans[k], tt + k);
}
}
cout << ans[n];
return 0;
}
22. MT2122 小码哥的跳棋游戏新编
(1)题目描述
小码哥喜爱跳棋。跳棋游戏在一条直线上,一共n个位置(1~n),每个位置有3个状态:0表示没有棋子,1表示红棋子,2表示蓝棋子。在起始的点(坐标为0)没有棋子。小码哥的棋子自然是能通过没有棋子的位置。当面前有1个棋子时,小码哥可以直接跳过。当有两个及以上不同颜色棋子连在一起时,小码哥的棋子是跳不过去的。这时候,就要花费能量,破坏掉一些棋子,才能跳过。但小码哥的棋子是经过升级的,如果一连串相同颜色的棋子在一起时,小码哥是可以直接跳过的。已知破坏一枚棋子需要花费一点能量。现在要求小码哥到达终点(坐标为n+1)需要花费至少多少能量?
格式
输入格式:
第1行包含一个正整数n
第2行n个整数ai(1<=i<=n),表示棋盘的状态
.
输出格式: 一个整数,输出最小耗费的能量数
样例1
输入:
5
0 1 1 0 0
.
输出: 0
备注:
3<=n<=10的5次
(2)参考代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 7;
int a[N];
int main()
{
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
int ans = 0;
for (int i = 1; i <= n; i++) {
if (a[i] && a[i - 1] && a[i] != a[i - 1]) {
a[i] = 0;
ans++;
}
}
cout << ans << endl;
return 0;
}
23. MT2123 小码哥的布阵指挥
(1)题目描述
小码哥手上有n个部队,驻扎在一条马路上,可以把这个马路看作一条x轴,小码哥的指挥所在原点,第i个部队在α[团]的位置,现在重新布置部队的位置,设布置后的位置为b[团],要求b[团]≥ a[列],且每个部队之间的距离大于等于X;
由于部队是从第1个开始移动的,所以你需要做出对于每只部队来说,当前情况下最优的解(当前情况指,序号在他之前的部队已经更新过后的情况),即在满足上述条件下,部队离小码哥越近越好。
格式
输入格式:
第一行两个整数n, ac ;
第二行n个整数,表示a[i];
.
输出格式: 一行,输出b[i],空格分开
样例1
输入:
4 11
1 21 11 7
.
输出: 1 21 32 43
备注:
提示:1≤n ≤1000,1 ≤ a, a[i],b[i]≤10的6次
(2)参考代码
#include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
typedef unsigned long long int ull;
#define rall(a) a.rbegin(), a.rend()
#define fi first
#define se second
#define rep(i,s,n) for(int i=s;i<n;i++)
#define repd(i,s,n) for(int i=s;i>=n;i--)
const int MOD=1e9+7;
const int maxN=5e3+1;
const int INF=2e9;
const int MB=20;
const int MAX_LEN=200001;
ull s[MAX_LEN];
ull p[MAX_LEN];
void solve()
{
ll n,x;
cin>>n>>x;
rep(i,0,n)
{
cin>>s[i];
p[i]=s[i];
}
ll pre=s[0];
rep(i,1,n)
{
sort(p,p+i);
ll temp;
rep(j,0,i)
{
temp=p[j]+x;
if (j==i-1)
break;
else
{
if (p[j+1]-temp>=x)
break;
}
}
s[i]=temp>s[i]?temp:s[i];
p[i]=s[i];
}
rep(i,0,n)
cout<<s[i]<<" " ;
}
int main()
{
solve();
return 0;
}
24. MT2124 活动分组
(1)题目描述
小码哥正在组织进行一次团建活动,共有n人参加。活动分为A、B两个项目,每个项目都需要两人组队参加。假设每个人有两个能力值a,b 分别表示对A、B两个项目的擅长程度,为了使活动圆满进行,小码哥希望每一组的两个人A能力值之和等于B能力值之和。请你帮忙计算是否有一种分组的可能满足小码哥的想法。
格式
输入格式:
输入共三行,第一行一个偶数n ∈ [2,1×10]表示总人数,第二行n个正整数表示每个人A项目的能力值,第三行n个正整数表示每个人B项目的能力值。
.
输出格式: 一行,如果存在这样的分组输出Yes,否则输出No
样例1
输入:
6
1 2 3 4 5 6
6 5 4 3 2 1
.
输出: YES
备注:
第一个和第六个一组,第二个和第五个一组,第三个和第四个一组,这样每组的A项目能力值之和均为7,B项目能力值之和均为7。
(2)参考代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<int, int>;
pii p[100005];
multiset<pii> s;
int main() {
int n;
ll suma = 0, sumb = 0;
cin >> n;
int m = n / 2;
for (int i = 1; i <= n; ++i) {
cin >> p[i].first;
suma += p[i].first;
}
for (int i = 1; i <= n; ++i) {
cin >> p[i].second;
sumb += p[i].second;
}
if (suma % m != 0 || sumb % m != 0) {
cout << "NO" << endl;
return 0;
}
suma /= m, sumb /= m;
for (int i = 1; i <= n; ++i) {
s.insert(p[i]);
}
for (int i = 1; i <= n; ++i) {
if (s.empty()) break;
if (s.find(p[i]) == s.end())
continue;
auto it = s.find(p[i]);
s.erase(it);
pii q = {suma - p[i].first, sumb - p[i].second};
if (s.find(q) == s.end()) {
cout << "NO" << endl;
return 0;
}
it = s.find(q);
s.erase(it);
}
cout << "YES" << endl;
}
25. MT2125 外卖递送
(1)题目描述
小码哥又被安排去建设外卖站了,所有居民的居住地点α都在一条x轴上,小码哥为了使送外卖移动的距离最短,现在在选择设置外卖站的位置。求把站建在何处,可以使得站到每家居民的距离之和最小。
注:不保证居民的居住地点a唯一,即可以两个居民的居住地点相同,允许外卖站设在居民点。
格式
输入格式: 第一行一个整数N,第二行N个整数A~ AN 。
.
输出格式: 输出一个整数,表示距离之和的最小值。
样例1
输入:
4
6 2 9 1
.
输出: 12
备注:
提示:对于100%的数据:1<N ≤100000, abs(ai)≤10000000
(2)参考代码
#include<bits/stdc++.h>
using namespace std;
int n1[100005],n2[100005];
int main(){
int n;
long long sum=0;
cin>>n;
for(int i=0;i<n;i++)
cin>>n1[i];
sort(n1,n1+n);
for(int i=n-1;i>=0;i--){
n2[n-1-i]=n1[i];}
for(int i=0;n1[i]<n2[i];i++)
sum+=n2[i]-n1[i];
cout<<sum<<endl;
return 0;
}
结语
感谢大家一直以来的不断支持与鼓励,码题集题库中的进阶塔350题正在逐步更新,之后会逐步跟进星耀,王者的题,尽请期待!!!
同时,也希望这些题能帮助到大家,一起进步,祝愿每一个算法道路上的“苦行僧”们,都能够历经磨难,终成正果,既然选择了这条路,走到了这里,中途放弃,岂不是太过可惜?
另附中国计算机学会的杰出会员、常务理事轩哥博士的B站视频讲解链接https://space.bilibili.com/518554541/?spm_id_from=333.999.0.0,供大家更好的进行学习与刷题~( ̄▽ ̄~)~
愿你的结局,配得上你一路的颠沛流离。