算法竞赛入门【码蹄集进阶塔335题】(MT2051-2075)
文章目录
- 算法竞赛入门【码蹄集进阶塔335题】(MT2051-2075)
- 前言
- 为什么突然想学算法了?
- 为什么选择码蹄集作为刷题软件?
- 目录
- 1. MT2051 矩阵01变
- 2. MT2052 矩形
- 3. MT2053 切蛋糕
- 4. MT2054 信号灯
- 5. MT2055 背军理
- 6. MT2056 最大异或和
- 7. MT2057 二阶前缀和
- 8. MT2058 门票
- 9. MT2059 最大的平均值
- 10. MT2060 数列分割
- 11. MT2061 小码哥的三色墙
- 12. MT2062 黑白双煞
- 13. MT2063 新全排列
- 14. MT2064 数三角形
- 15. MT2065 palace
- 16. MT2066 区间修改
- 17. MT2067 相对马高
- 18. MT2068 小码哥的地毯
- 19. MT2069 高数考试
- 20. MT2070 等差
- 21. MT2071 小码哥剪绳子
- 22. MT2072 咖啡品鉴师小码哥
- 23. MT2073 养竹鼠
- 24. MT2074 奶牛排队
- 25. MT2075 伐木工小码哥
- 结语
前言
为什么突然想学算法了?
> 用较为“官方”的语言讲,是因为算法对计算机科学的所有分支都非常重要。 在绝大多数的计算机科学分支领域中,要想完成任何实质性的工作,理解算法的基础知识并掌握与算法密切相关的数据结构知识是必不可少的。
> 但从实际而言,是因为当下快到了考研和找工作的年纪(ಥ_ಥ),无论走哪一条路,都不免需要一些相对丰富的算法知识,是故,便产生了一个暑假速成算法的计划,可能对于像我这种算法竞赛小白而言,几乎很难,但我仍然还是想尝试一下,毕竟,梦想还是要有的,万一实现了呢?~( ̄▽ ̄~)~
为什么选择码蹄集作为刷题软件?
码蹄集,是在全国高等学校计算机教学与产业实践资源建设专家委员会(TIPCC) 指导下建设的,其依托全国各大名校计算机系和清华大学出版社等单位的强大资源,旨在为计算机学习爱好者提供全面和权威的计算机习题。
目录
1. MT2051 矩阵01变
(1)题目描述
格式
输入格式:
第一行两个正整数N ,M(N ,M ≤100)
接下来n行,每行m个数表示矩阵
.
输出格式: 按照题目要求输出答案矩阵
样例1
输入:
5 4
1100
0000
0000
0000
0000
.
输出:
1 2 2 2
1 2 2 2
1 2 2 2
1 2 2 2
1 2 2 2
(2)参考代码
#include <iostream>
using namespace std;
int main() {
int n,m,a[105][105],b[105][105];
cin>>n>>m;
for(int i=0;i<105;i++){
for(int j=0;j<105;j++){
a[i][j] = 0;
b[i][j] = 0;
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
char c;
cin>>c;
a[i][j] = c-'0';
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
b[i][j] = b[i-1][j]+b[i][j-1]-b[i-1][j-1]+a[i][j];
cout<< b[i][j] << " ";
}
cout<<endl;
}
return 0;
}
2. MT2052 矩形
(1)题目描述
给定一个N * M的矩阵,1表示已经占用了,0表示没有被占用,求一个由0构成的矩阵,使其周长最大。
格式
输入格式:
第一行两个整数n, m 含义如上,接下来n行每行m个数表示这个矩阵
.
输出格式: 输出一个数,表示最大周长
样例1
输入:
3 3
000
010
000
.
输出: 8
备注:
其中: 1≤n, m ≤25
(2)参考代码
h = [0 for _ in range(25)]
l = [0 for _ in range(25)]
r = [0 for _ in range(25)]
output = 0
def solve(col):
global output
for i in range(col):
if h[i]==0:
continue
for j in range(i,-1,-1):
if h[j]>=h[i]:
l[i] = j
else:
break
for j in range(i,col):
if h[j]>=h[i]:
r[i] = j
else:
break
for i in range(col):
output = max(output,h[i]*2+(r[i]-l[i]+1)*2)
def main():
row,col =list(map(int,input().split()))
dp = [[0 for _ in range(col)] for _ in range(row)]
for i in range(row):
temp = str(input())
for j in range(col):
dp[i][j] = int(temp[j])
for i in range(row):
for j in range(col):
count = 0
y = i
while 1:
if dp[y][j]==0:
count+=1
y+=1
if y==row:
break
else:
break
h[j] = count
solve(col)
print(output)
if __name__ == '__main__':
main();
3. MT2053 切蛋糕
(1)题目描述
今天是小码哥的生日,家里人给他买了一块蛋糕。这块蛋糕被用不同色彩分成了n个相同的部分,每一部分上面的水果都不同。由于小码哥对水果很有研究,他给每个水果都打了分。
小码哥希望能吃到最多分数,但他又只能吃m块(m≤n) 。
请你帮他从这n小块中找出连续的k(1≤k ≤m)块蛋糕,使得其上的总分数最大。
格式
输入格式:
第一行两个整数n,m。分别代表共有n个部分,小码哥最多只能吃m块。
第二行n个整数,第i个整数代表第i 部分的分数。
.
输出格式: 仅一行一个整数,即小码哥能够得到的最大分数。
样例1
输入:
6 3
1 -2 3 -4 5 -6
.
输出: 5
备注:
对于20%的数据,有1≤n ≤100
对于100%的数据,有1≤n≤5 * 105, i个整数的绝对值小于等于500
(2)参考代码
#include<bits/stdc++.h>
using namespace std;
int sum[500100];
int a[500100];
struct Node{
int post,date;
}sta[500100];
int main(){
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
sum[i]=sum[i-1]+a[i];//前缀和
}
int ans=0;
int head=0;
int M=-1e9;//可能有负数,设为负值
for(int i=1;i<=n;i++){
while(head<=ans&&sta[ans].date>=sum[i]){
ans--;//出队
}
sta[++ans].date=sum[i];//入队
sta[ans].post=i;
while(head<=ans){
if(sta[head].post>=i-m&&sta[head].post<=i){
M=max(M,sum[i]-sta[head].date);
break;
}
else{
head++;
}
}
}
printf("%d\n",M);
return 0;
}
4. MT2054 信号灯
(1)题目描述
小码哥的农场有很多牛,这些牛很聪明,它们想和人一样过马路,于是小码哥给它们修建了马路。但因为牛又不是那么聪明,他们有时会撞坏信号灯,小码哥很苦恼。
于是他想找你帮忙算一下让连续k个信号灯有用最少要修几个。
共有NⅣ个信号灯,编号为1~N,有B个信号灯损坏,给你它们的编号。
问,最少修好几个信号灯,可以有K个编号连续的信号灯。
格式
输入格式:
第一行输入三个正整数N,K,B(1≤B,K ≤N)接下来B行每行一个整数描述破损的灯的编号。
.
输出格式: 求出最少所需修复的信号灯数
样例1:
输入:
10 6 5
2
10
1
5
9
.
输出:1
备注:
其中:1<N ≤ 100000
(2)参考代码
#include <bits/stdc++.h>
using namespace std;
inline int read(){
int f=1,x=0;
char ch=getchar();
while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return f*x;
}
int main(){
int k,n,b;
int re[100005]={0};
cin>>n>>k>>b;
for(int i=1;i<=b;i++){
int temp=0;
cin>>temp;
re[temp]=1;
}
int min_time = 1000000;
int time=0;
for(int j=1;j<=n;j++){
if(j<=k){
time+=re[j];
if(j==k) min_time = min(min_time,time);
}
if(j>k){
time = time + re[j]-re[j-k];
min_time = min(min_time,time);
}
}
cout<<min_time;
return 0;
}
5. MT2055 背军理
(1)题目描述
天哪天哪,军理有好多知识需要背呀。老师已经在厚厚的军理书上(该书一共有n页)画上了若干重点。我们现在已经知道,第i页有a;个重点。
现在小码哥向你询问了q次,每次询问给定一个闭区间[a,g],请你输出从第x页开始到第y页结束,这些页数之间一共有多少重点。
格式
输入格式:
第一行,一个整数n。表示书的总页数(书页码编号从第一页开始)
接下来一行,输入n个整数a,表示该页上的老师画的重点个数。
接下来一行一个整数q,表示小码哥的询问次数。
接下来q行,每行两个整数x、y,表示小码哥询问的区间
.
输出格式: 输出q行,每行一个数,表示从第a 页开始到第y页结束,这些页数之间一共有多少重点。
样例1
输入:
10
1 3 5 7 9 8 6 4 2 6
4
1 4
2 3
1 10
6 7
.
输出:
16
8
51
14
备注:
数据范围: 1≤n,q≤100000, ai小于等于100
(2)参考代码
def main():
#code here
n=int(input())
nums=list(map(int,input().split()))
q=int(input())
def lowbit(i):
return i&(-i)
def query(i):
res=0
while i>0:
res += base[i]
i -= lowbit(i)
return res
def add(i,val):
while i<=n:
base[i] += val
i += lowbit(i)
base = [0] * (n+1)
for i in range(n):
add(i+1,nums[i])
for _ in range(q):
l,r=map(int,input().split())
res=query(r)-query(l-1)
print(res)
if __name__ == '__main__':
main();
6. MT2056 最大异或和
(1)题目描述
给定一串手链,每个珠子被赋予一个价值w; ,现要从中截取连续的一段,使得他们的异或和最大(注意,手链还未串上)。
格式
输入格式:
第1行包含一个正整数N
第2行n个正整数u, ,表示珠子价格
.
输出格式: 一个正整数,输出最大异或和
样例1
输入:
5
1 2 3 4 5
.
输出: 7
备注:
其中: n≤2000, u;≤100000
(2)参考代码
import java.util.Scanner;
public class Main {
private static int solve(int[] data) {
if (data == null || data.length == 0) {
return 0;
}
Trie trie = new Trie();
trie.add(0);
int sum = 0;
int ret = data[0];
for (int i = 0; i < data.length; ++i) {
sum ^= data[i];
ret = Math.max(ret, trie.query(sum));
trie.add(sum);
}
return ret;
}
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
while (in.hasNext()) {
int n = in.nextInt();
int[] arr = new int[n];
for (int i = 0; i < n; ++i) {
arr[i] = in.nextInt();
}
System.out.println(solve(arr));
}
}
}
class Trie {
private Node root;
class Node {
Node[] children = new Node[2];
}
public Trie() {
this.root = new Node();
}
public void add(int n) {
Node cur = this.root;
for (int i = 31; i >= 0; --i) {
int digit = (n >> i) & 1;
if (cur.children[digit] == null) {
cur.children[digit] = new Node();
}
cur = cur.children[digit];
}
}
public int query(int n) {
Node cur = this.root;
int ret = 0;
for (int i = 31; i >= 0; --i) {
int digit = (n >> i) & 1;
int best = i == 31 ? digit : digit ^ 1;
if (cur.children[best] == null) {
best ^= 1;
}
ret |= ((best ^ digit) << i);
cur = cur.children[best];
}
return ret;
}
}
7. MT2057 二阶前缀和
(1)题目描述
在一个直角坐标系上,有 n个坐标上有元素值(其余坐标的元素值为0),现给定一些点的坐标(azi, 3i)和 这个坐标的元素值u;,计算用一个边长为R的正矩形能囊括坐标的元素值的和的最大值。不包括正方形的边界。
格式
输入格式:
输入文件的第一行为两个正整数n和R ,
接下来的n 行每行有3个自然数,分别表示i,Ji,Ui 。
.
输出格式: 输出文件仅有一个正整数,表示一个正方形最多能囊括地图上的最大的元素值之和。
样例1
输入:
2 1
0 0 1
1 1 1
.
输出: 1
备注:
数据不保证给出的坐标都不相同,如果有相同坐标的输入,请将他们的vj进行累加,视做一个坐标。
1≤n,R≤1000,0 ≤i,Ji ≤1000,0 ≤vi ≤10的8次
正方形的边必须与a,y轴平行。
(2)参考代码
#include <algorithm>
#include <vector>
#include <string>
#include <map>
#include <queue>
#include <stack>
#include <queue>
#include <iostream>
#include <cmath>
#include <cstring>
#include <set>
#include <iomanip>
#include <stdexcept>
#include <fstream>
#define ll long long
#define endl '\n'
using namespace std;
const int INF=0x3f3f3f3f;
const int N=6e5+7;
const double pi=acos(-1);
ll x[N],p[N];
ll n,m;
ll d[N];//D数组要开到三倍长度
ll maxx=-INF,minn=INF;
bool flag=false;
struct node{
ll pos,val;//位置,差值
};
bool cmp(node a,node b){
if (a.pos!=b.pos) return a.pos<b.pos;//照左端点排序
else return a.val<b.val;
}
void updated(ll pos,ll val){
if(val<=m) return;
maxx=max(maxx,pos+val-m);
minn=min(minn,pos+m-val);
}
void slove(){
int t;
cin>>t;
while(t--){
maxx=-INF,minn=INF;
vector<node>v;
cin>>n>>m;
v.push_back({-INF,0});
v.push_back({INF,0});//因为下面的前缀和需要从1开始
for(int i=1;i<=n;++i){
cin>>x[i]>>p[i];
v.push_back({x[i]-p[i]+1,1});//前面的差分值为1
v.push_back({x[i]+1,-2});//差分值为-2
v.push_back({x[i]+p[i]+1,1});//差分值为1
}
sort(v.begin(),v.end(),cmp);
int pre=0;//记录各个点的降水量的前缀和用于算出后面点的降水量
for(int i=1;i<v.size()-1;++i){
d[i]=d[i-1]+v[i].val;
if(v[i].pos!=v[i+1].pos){
ll val=pre+d[i];//两次前缀和,可以算出水位
updated(v[i].pos, val);
val=pre+d[i]*(v[i+1].pos-v[i].pos);//前缀和
updated(v[i+1].pos-1, val);//比较下一个点的上面那个点因为可能v[i]是一个上升段,
}
pre+=d[i]*(v[i+1].pos-v[i].pos);
}
for(int i=1;i<=n;++i){
if((x[i]+p[i])>=maxx&&minn>=(-p[i]+x[i])) flag=true;
else cout<<"0";
}
}
if(flag==true) cout<<"1";
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
slove();
return 0;
}
8. MT2058 门票
(1)题目描述
小码哥家附近的一个主题公园最近搞了一个活动,小码哥在活动中获得了一等奖。而一等奖是一长串的纸条,上面有n个格子,每个格子里有一个数字,活动人员告诉小码哥,只要他裁下一段连续的格子,格子内数字的总和/裁下的格子的数量大于等于门票费t,那么小码哥就能用这段格子抵消门票费。
小码哥想要尽可能多的利用这个奖品,为此他想先知道有多少种裁剪的方案能裁出一段能抵消门票费的格子。
由于方案数可能很大所以需要你输出方案数对1e9+7取模的结果。
格式
输入格式:
第一行一个正整数n,t,表示格子的数量和门票费。
第二行n个整数ai,表示格子内的数字。
.
输出格式: 一个整数,表示总方案数对1e9+7取模的结果。
样例1
输入:
5 4
3 4 2 3 5
.
输出: 3
(2)参考代码
#include <bits/stdc++.h>
#define int long long
#define mp make_pair
#define eb emplace_back
#define fi first
#define se second
using namespace std;
using cd = complex <double>;
const long long INF = 1e15;
const int mod = 998244353;//1e9 + 7;//786433;
const double Pi = acos(-1);
void Fastio()
{
ios_base::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
}
int n, k;
long long a[200005];
long long ans = 0;
long long BIT[200005];
vector <long long> Val;
void Update(int node, long long val)
{
for(node; node <= n + 1; node += node & -node)
{
BIT[node] += val;
}
}
long long Get(int node)
{
long long ans = 0;
for(node; node > 0; node -= node & -node)
{
ans += BIT[node];
}
return ans;
}
signed main()
{
Fastio();
cin >> n >> k;
Val.eb(0);
for(int i = 1; i <= n; i++)
{
cin >> a[i];
a[i] -= k;
a[i] += a[i - 1];
Val.eb(a[i]);
}
sort(Val.begin(), Val.end());
for(int i = 0; i <= n; i++)
{
a[i] = lower_bound(Val.begin(), Val.end(), a[i]) - Val.begin() + 1;
}
for(int i = 0; i <= n; i++)
{
ans += Get(a[i]);
Update(a[i], 1);
}
cout << ans;
}
9. MT2059 最大的平均值
(1)题目描述
给一个数组,长度为n,找一个长度大于等于m的子区间,使这个区间的元素的平均值最大。
格式
输入格式:
第一行输入整数n 和m,数据间用空格隔开。
接下来n行,每行输入一个整数a
.
输出格式: 输出一个整数,表示平均值的最大值乘以1000再向下取整之后得到的结果。
样例1
输入:
10 6
6
4
2
10
3
8
5
9
4
1
.
输出: 6500
备注:
提示: 1≤n ≤100000,1 ≤m ≤n,0≤ai≤ 1e7
(2)参考代码
#include <bits/stdc++.h>
using namespace std;
#define MAX 2000010
#define ll long long
ll n,m,mid,l,r;
ll a[MAX],b[MAX],sum[MAX];
ll check(ll x){
for(ll i=1;i<=n;i++){
b[i] = a[i]-x;
sum[i] = sum[i-1]+b[i];
}
ll y=MAX;
for(ll i=m;i<=n;i++){
y=min(y,sum[i-m]);
if(sum[i]-y>=0) return 1;
}
return 0;
}
int main(){
scanf("%lld %lld",&n,&m);
l=MAX,r=0;
for(ll i=1;i<=n;i++){
scanf("%lld",&a[i]);
a[i]*=1000;
l=min(l,a[i]);
r=max(r,a[i]);
}
while(l!=r){
mid=(l+r+1)/2;
if(check(mid)) l=mid;
else r=mid-1;
}
printf("%lld\n",l);
return 0;
}
10. MT2060 数列分割
(1)题目描述
小码哥给你一个长度为n的数列,求将该数列分割成两个左右两个部分且两个部分内数字的和相等的方案数。
格式
输入格式:
给定一整数n,接下来有n个数a[团]1≤n ≤105, a[i]的绝对值不大于10000
.
输出格式: 输出一行表示答案。
样例1
输入:
9
1 5 -6 7 9 -16 0 -2 2
.
输出: 3
(2)参考代码
#include <bits/stdc++.h>
using namespace std;
int a[100010];
long long b[100010],s=0;
int main()
{
int i,j,k,m,n,tot=0;
scanf("%d",&n);
b[0]=0;
for(i=1;i<=n;i++){
cin>>a[i];
s+=a[i];
b[i]=b[i-1]+a[i];
}
s=s/2;
for(i=1;i<n;i++){
if(b[i]==s) tot++;
}
cout<<tot;
return 0;
}
11. MT2061 小码哥的三色墙
(1)题目描述
小码哥的房间墙要重新刷漆。墙面大小可以看成n * m个小方块,每个方块初始颜色为W,B或R,小码哥对于刷漆的要求是:
1.从最上方若干行(至少一行)的格子全部是W色的;
2.接下来若干行(至少一行)的格子全部是B色的;
3.剩下的行(至少一行)全部是R色的;
小码哥想要更改最少的格子数来满足要求。请你帮他算算最少的更改数。
格式
输入格式:
第一行,两个整数n,m ;
接下来n行矩阵,每个方块用W,B,R来代表颜色。
.
输出格式: 一个整数,表示至少需要更改多少块的颜色。
样例1
输入:
4 5
WRWRW
BWRWB
WRWRW
RWBWR
.
输出: 11
(2)参考代码
#include <bits/stdc++.h>
using namespace std;
int n,m,ans=0x7fffffff,w[5100],r[5100],b[5100];
string s;
inline int check(char c){
int tot=0;
for(int i=0;i<m;++i) if(s[i]!=c)++tot;
return tot;
}
int main() {
cin>>n>>m;
for(int i=1;i<=n;++i){
cin>>s;
w[i]=w[i-1]+check('W');
b[i]=b[i-1]+check('B');
r[i]=r[i-1]+check('R');
}
for(int i=1;i<n-1;++i){
for(int j=i+1;j<n;++j)
ans = min(ans,w[i]+b[j]-b[i]+r[n]-r[j]);
}
cout<<ans;
return 0;
}
12. MT2062 黑白双煞
(1)题目描述
小码哥现在手里一个环(首尾相连),环上有n(2≤n ≤100000)个点,每个点必定是两种颜色之一(黑色或白色)
现在他想请你进行一些操作,每次交换两个点的位置,求最少操作次数使得黑白色的n个点分离成两个部分,每个部分只含一种颜色。
格式
输入格式:
第一行一个正整数n (2≤n ≤100000)
接下来一行一个字符串S,长度为n,第i位为H则表示黑点,T则表示白点。
.
输出格式: 按题目要求输出一行一个整数,表示最小交换次数。
样例1
输入:
9
HTHTHTHHT
.
输出: 2
(2)参考代码
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+100;
char s[N];
int a[N*2];
int main(){
int n;
cin>>n;
cin>>s+1;
int h=0,t=0;
for(int i=1;i<=n;i++){
if(s[i]=='H') h++;
else a[i]++,a[i+n]++;
}
for(int i=1;i<=n*2;i++) a[i]+=a[i-1];
int ans = 0x3f3f3f3f;
for(int i=h;i<=n*2;i++) ans=min(ans,a[i]-a[i-h]);
cout<<ans<<endl;
return 0;
}
13. MT2063 新全排列
(1)题目描述
格式
输入格式:
第一行输入n(2 ≤n ≤2* 10的5次)原序列长度。
第二行输入q1, 42,43,. . . , 4n-1(一n<qi<n)。
.
输出格式:
若原序列不存在,输出-1
若存在,输出第一个数最小的原序列
样例1
输入:
3
-2 1
.
输出: 3 1 2
(2)参考代码
def main():
#code here
n = int(input())
arr = list(map(int,input().split()))
s = [0] + arr
for i in range(1,n):
s[i] += s[i-1]
if len(set(s)) != n:
print(-1)
return
mn = min(s[1:])
if mn > 0:
first = 1
else:
first = 1-mn
for v in s:
print(first + v,end=" ")
if __name__ == '__main__':
main();
14. MT2064 数三角形
(1)题目描述
给出四个整数A,B,C,D ,其中1≤A≤B≤C≤D≤5·105
求三边长分别为a, y,z(z, g,z为整数)并且满足A≤a<B≤y<C≤7≤D的非退化三角形(三顶点不共线)的个数
格式
输入格式: 第一行为A,B,C,D
.
输出格式: 输出一个整数为答案
样例1
输入: 1 2 3 4
.
输出: 4
备注:
其中: A<B≤C<D≤5e5
(2)参考代码
#include <bits/stdc++.h>
using namespace std;
const int N=5e5+100;
int cnt[N];
int main(){
int a,b,c,d;
cin>>a>>b>>c>>d;
for(int i=c;i<=d;i++) cnt[i]++;
for(int i=1;i<N;i++) cnt[i]+=cnt[i-1];
long long ans=0;
for(int i=a+b;i<=b+c;i++){
int xmin = max(i-c,a),xmax=min(b,i-b);
ans+=1ll*(xmax-xmin+1)*cnt[i-1];
}
cout<<ans<<endl;
return 0;
}
15. MT2065 palace
(1)题目描述
小码哥准备给自己盖一座很华丽的宫殿。于是,他看中了一块 N x M的矩形空地。
空地中每个格子都有自己的海拔高度。小码哥想让他的宫殿的平均海拔在海平面之上(假设海平面的高度是0,平均数都会算吧? )。而且,小码哥希望他的宫殿尽量大,能够容纳更多的人来膜拜他。请问小码哥的宫殿最后会有多大?
格式
输入格式:
第一行两个整数n, m
之后n行,每行m个数,描述的空地的海拔。
.
输出格式: 输出宫殿最大面积。
样例1
输入:
3 2
4 0
-10 8
-2 -2
.
输出: 4
备注:
其中: 1≤m≤n ≤200,—100≤空地海拔≤100
(2)参考代码
#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#define reg register
using namespace std;
typedef long long ll;
const int N=205;
int n,m,ans;
ll a[N][N],s[N][N],t[N];
inline ll read()
{
ll x=0; bool w=1;
char c=getchar();
while (!isdigit(c)&&c!='-') c=getchar();
if (c=='-') c=getchar(),w=0;
while (isdigit(c))
{
x=(x<<1)+(x<<3)+c-'0';
c=getchar();
}
return w?x:-x;
}
inline bool check(int x)
{
ll res=0;
for (reg int i=x;i<=m;i++)
{
res=min(res,t[i-x]);
if (t[i]>res) return 1;
}
return 0;
}
inline int getlen()
{
int l=1,r=m,len=0;
while (l<=r)
{
int mid=(l+r)>>1;
if (check(mid)) len=mid,l=mid+1;
else r=mid-1;
}
return len;
}
int main()
{
n=read(),m=read();
for (reg int i=1;i<=n;i++)
for (reg int j=1;j<=m;j++)
s[i][j]=s[i-1][j]+(a[i][j]=read());
for (reg int i=1;i<=n;i++)
for (reg int j=i;j<=n;j++)
{
for (reg int k=1;k<=m;k++) t[k]=t[k-1]+s[j][k]-s[i-1][k];
ans=max(ans,(j-i+1)*getlen());
}
printf("%d\n",ans);
return 0;
}
16. MT2066 区间修改
(1)题目描述
小码哥有n个手下,他们的编号为[1,n]。小码哥想要给他们发放工资,用一个长度为n的数列a[团],表示编号为i的手下初始收到的工资,但发工资时没有计算好,导致每个人到手的工资不一样,为了使他们的工资相同,现在小码哥可以使得编号在区间[1, r]中的手下,收到1或上交1的工资。即工资+1 或工资-1 ;
求至少需要多少次操作才能使所有人的工资相同都一样;
工资可以暂时为负数,小码哥可以压榨他的手下,让手下暂时垫付部分钱,之后再还;
小码哥也可以多付一些钱给手下,也可以少给一些(自己贪污一点) ,即工资总数不要求一定是初始手下的工资之和。只要他们每个人的工资一样就可以。
格式
输入格式:
第一行一个正整数n。
接下来n行,每行一个整数,第i+1行的正整数表示a[i];
.
输出格式: 第一行输出最少操作次数。
样例1
输入:
4
3
2
3
4
.
输出: 2
备注:
其中: 1≤n ≤100000,0≤a[i]≤10000000
(2)参考代码
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
#define ll long long
const ll N = 1e5 + 1e4;
ll a[N] , b[N];
int main()
{
int n;
ll ans = 0, buf = 0;
cin >> n ;
for(int i = 1; i <= n; i++){
cin >> a[i] ;
}
for(int i = 2; i <= n; i++) {
b[i] = a[i] - a[i-1];
if(b[i] > 0) ans += b[i];
else buf -= b[i];
}
ll ma = max (ans, buf) ;
cout << ma ;
return 0;
}
17. MT2067 相对马高
(1)题目描述
小码哥有n头马,他们站作一排,由于马的身高不同,如果马A和B能够相互看见,那么,在(a,b)区间的马的身高都小于a,b 马。
现在给出最高的马的身高h。之后小码哥将给出f组数据,表示马A,B可以相互看见,请你推出小码哥的马们的最大的可能的身高,由于小码哥工作向来随性,马的关系之间可能有重复(数据不确保α<b )
格式
输入格式:
第1行输入n, h, f
第2行到第f+1行输入两个数据a,b,以空格分隔
.
输出格式: n行,输出每头马的最大可能身高
样例1
输入:
20 1000 6
5 14
16 20
9 11
17 18
6 7
2 4
.
输出:
1000
1000
999
1000
1000
999
999
999
999
998
999
999
999
1000
1000
1000
999
999
999
1000
备注:
(1<n s 10,000),(0<f ≤10,000),(1 ≤A,B ≤N)(1≤H≤1, 000,000),马的身高一定为整数
(2)参考代码
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <set>
using namespace std;
#define ll long long
#define N 21000
ll a[N] = {}, b[N] = {};
struct op{
int a, b;
}s[N];
bool operator < (const op &a, const op &b){
if (a.a == b.a) return a.b < b.b ;
else return a.a < b.a;
}
set<op> p ;
int main(){
ll n, h, m ;
cin >> n >> h >> m;
for(ll i = 1; i <= m; i++){
ll a1,a2;
cin >> a1 >> a2;
if (a1 == a2) continue;
if (a1 > a2) swap(a1,a2);
op f; f.a = a1, f.b = a2 ;
p.insert(f);
}
for (set<op>::iterator i = p.begin(); i != p.end();i++){
op f = *i;
b[f.a + 1] = b[f.a + 1] - 1;
b[f.b] = b[f.b] + 1 ;
}
for (ll i = 1;i <= n; i++){
a[i] = b[i] + a[i - 1] ;
cout << a[i] + h << endl;
}
return 0;
}
18. MT2068 小码哥的地毯
(1)题目描述
小码哥的房间要铺上地毯。房间大小为n *n ,小码哥有m块矩形地毯,给出每块地毯放置的两个对角的坐标。问每个点被多少个地毯覆盖。
格式
输入格式:
第一行,两个整数n,m ;
接下来m行,每行两个坐标 (a1, J1)和(z2, g2)代表一块地毯,左上角是(z1, g1),右下角是(r2,92)。
.
输出格式:
输出n行,每行n个整数。
第i行第j列的整数表示(i,j)这个格子被多少个地毯覆盖。
样例1
输入:
5 3
2 2 3 3
3 3 5 5
1 2 1 4
.
输出:
0 1 1 1 0
0 1 1 0 0
0 1 2 1 1
0 0 1 1 1
0 0 1 1 1
备注:
对于30%的数据,有1≤n, m ≤100
对于100%的数据,有1≤n, m ≤1000,1≤ 1,C2,91,9J2 ≤n
(2)参考代码
#include <bits/stdc++.h>
using namespace std;
int m,n;
int a[1024][1024];
int res[1024][1024];
int main(){
cin>>n>>m;
memset(a,0,sizeof(a));
memset(res,0,sizeof(res));
while(m--){
int x1,y1,x2,y2;
cin>>x1>>y1>>x2>>y2;
for(int i=x1;i<=x2;i++){
a[i][y1]++;
a[i][y2+1]--;
}
}
for(int i=1;i<=n;i++){
int now=0;
for(int j=1;j<=n;j++){
now+=a[i][j];
res[i][j]=now;
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(j>1) cout<<" ";
cout<<res[i][j];
}
cout<<endl;
}
return 0;
}
19. MT2069 高数考试
(1)题目描述
高数考完了,又到了数学老师难办的时候。数学老师想捞同学一把,她总是要一遍遍地给某些同学增加分数(均为正整数),又要注意最低分是多少。由于工作量很大,你能帮帮她吗?
格式
输入格式:
第一行有两个整数n,p,代表学生数与增加分数的次数。
第二行有n个数, a1~ an,代表各个学生的初始成绩。
接下来p行,每行有三个数a,y,z,代表给第α个到第y个学生每人增加z分。
.
输出格式: 输出仅一行,代表更改分数后,全班的最低分。
样例1
输入:
3 2
1 1 1
1 2 1
2 3 1
.
输出: 2
(2)参考代码
#include<stdio.h>
#include<malloc.h>
int main()
{
int n,p;
scanf("%d%d",&n,&p);
int *a = (int*)malloc(sizeof(int)*n);
int *b = (int*)malloc(sizeof(int)*n);
for(int i=0;i<n;i++) {
scanf("%d",&a[i]);
if(i>0) b[i] =a[i] - a[i-1];
else b[i]=a[i];
}
for(int i=0;i<p;i++){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
b[x-1] += z;
if(y<n) b[y] -=z;
}
int min=b[0];
for(int i=1;i<n;i++){
b[i] += b[i-1];
if(b[i]<min) min=b[i];
}
printf("%d",min);
return 0;
}
20. MT2070 等差
(1)题目描述
学完等差数列的小码哥神清气爽,他想在一长串数中找到等差的部分。
学完等差数列的小码哥神清气爽,他想在一长串数中找到等差的部分。
格式
输入格式: 一个整数数组
.
输出格式: 返回一个非负整数
样例1
输入: 1 2 3 4
.
输出: 3
(2)参考代码
def main():
#code here
arr = list(map(int,input().split()))
n = len(arr)
dp = [0]*n
for i in range(2,n):
if arr[i]-arr[i-1] == arr[i-1]-arr[i-2]:
if dp[i-1] != 0:
dp[i] = dp[i-1] +1
else:
dp[i] = 3
tot = 0
for i in range(2,n):
if dp[i] >= 3:
tot += dp[i]-3+1
print(tot)
if __name__ == '__main__':
main();
21. MT2071 小码哥剪绳子
(1)题目描述
马上就要到文化节了,小码哥身为学生会的一员需要参与到道具制作。
由于被分配到趣味拔河,小码哥需要切绳子,有N条绳子,它们的长度分别为Li,…· ,Ln。如果从它们中切割出K条长度相同的绳子,这K条绳子每条最长能有多长?
格式
输入格式:
第一行两个整数N和K .
接下来N行,每行一个整数代表每条绳子的长度 Li。
.
输出格式: 一个整数代表切割后每条绳子的最大长度。
样例1
输入:
4 11
8
7
4
5
.
输出: 2
(2)参考代码
#include<bits/stdc++.h>
using namespace std;
int a[100010],n,k;
int test(int mid){
int cnt=0;
for(int i=0;i<n;i++) cnt+=a[i]/mid;
if(cnt>=k) return 1;
else return 0;
}
int main( )
{
cin>>n>>k;
for(int i=0;i<n;i++) cin>>a[i];
int l=1,r=100000,mid=(l+r)/2;
while(r-l>1){
if(test(mid)) l=mid;
else r=mid-1;
mid=(l+r)/2;
}
if(test(r)) cout<<r;
else cout<<l;
return 0;
}
22. MT2072 咖啡品鉴师小码哥
(1)题目描述
格式
样例1
输入:
3 2
1 2 3
3 2 1
.
输出: 1.667
(2)参考代码
#include<bits/stdc++.h>
using namespace std;
const double eps = 1e-8;
const int maxn = 1e6+5;
#define inf 0x3f3f3f3f;
#define ll long long;
int n,k;
int w[maxn],v[maxn];
double y[maxn];
bool cmp(int a,int b){
return a>b;
}
bool check(double x){
for(int i=0;i<n;i++) y[i] = v[i]-w[i]*x;
sort(y,y+n);
double sum=0;
for(int i=0;i<k;i++) sum+=y[n-i-1];
return sum>=0;
}
void solve(){
double l=0,r=inf;
double mid;
while(r-l>eps){
mid = (l+r)/2;
if(check(mid)) l=mid;
else r=mid;
}
printf("%.3f\n",r);
}
int main( )
{
while(~scanf("%d%d",&n,&k)){
for(int i=0;i<n;i++) cin>>v[i];
for(int i=0;i<n;i++) cin>>w[i];
solve();
}
return 0;
}
23. MT2073 养竹鼠
(1)题目描述
小码哥在饲养竹鼠,竹鼠棚里有Ⅳ个隔间,这些隔间分布在一条直线上,坐标是1,2, 3,. . . , 3n(0≤i≤100000000) 。
小码哥有M只竹鼠,为了防止竹鼠中暑,他要尽可能让相邻两只竹鼠相隔越远越好。所以,所有竹鼠中相隔最近的两只竹鼠的距离的最大值是多少(1<M<N≤50000 ) ?
格式
输入格式:
第一行,两个用空格隔开的数字Ⅳ和M。
后Ⅳ行,每行一个整数表示每个隔间的坐标,坐标是唯一的,也就是两只竹鼠位置不会重合。
.
输出格式: 输出一个整数,则竹鼠们最近距离的最大值。
样例1
输入:
5 3
1
2
4
8
9
.
输出: 3
(2)参考代码
#include<bits/stdc++.h>
using namespace std;
int n,c,a[100005],l,m,r;
bool check(int m){
int cnt=0;
int d;
int last=a[1];
for(int i=2;i<=n;i++){
d=a[i]-last;
if(d>=m){
cnt++;
last=a[i];
}
}
if(cnt+1<c) return true;
else return false;
}
int main( )
{
cin>>n>>c;
for(int i=1;i<=n;i++) cin>>a[i];
sort(a+1,a+n+1);
l=1;
r=a[n]-a[1];
while(l<=r){
m=(r+l)/2;
if(check(m)) r=m-1;
else l=m+1;
}
cout<<r<<endl;
return 0;
}
24. MT2074 奶牛排队
(1)题目描述
有一群奶牛排成一个圆环,小码哥想知道两只最远的奶牛到底隔了多远。奶牛A到B的距离为A顺时针走和逆时针走,到达B的较短路程。告诉你相邻两个奶牛间的距离,请你告诉小码哥两只最远的奶牛到底隔了多远。
格式
输入格式:
第一行一个整数N,表示有Ⅳ只奶牛。( 2≤N ≤100000 )接下来N行,第i行有一个数,表示第i-1头奶牛顺时针到第i头奶牛的距离( 1≤距离≤maalongint,距离和≤maulongint ) ,第Ⅳ行的数表示第Ⅳ头奶牛顺时针到第1头奶牛的距离。
.
输出格式: 一行,表示最大距离。
样例1
输入:
5
1
2
3
4
5
.
输出: 7
(2)参考代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
long long n,sum=0,ans=0;
long long a[100100];
int main()
{
scanf("%lld",&n);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]),a[i]+=a[i-1];
sum=a[n];
int l=1,r=2;
while(l<r && r<=n)
{
long long res1=a[r]-a[l],res2=sum-res1;
ans=max(ans,min(res1,res2));
if(res1>res2) l++;
else r++;
}
printf("%lld",ans);
fclose(stdin);
fclose(stdout);
return 0;
}
25. MT2075 伐木工小码哥
(1)题目描述
伐木工人小码哥需要砍M米长的木材。小码哥只被允许砍伐一排树(考虑到环保)。
小码哥的伐木机工作流程如下:小码哥设置一个高度参数H(米),伐木机升起一个巨大的锯片到高度H﹐并锯掉所有树比﹐H 高的部分。小码哥就得到树木被锯下的部分。
请帮助小码哥找到伐木机锯片的最大的整数高度H﹐使得他能得到的木材至少为M米。换句话说,如果再升高1米,他将得不到M米木材。
格式
输入格式:
第1行2个整数N和M,N 表示树木的数量,M 表示需要的木材总长度。
第2行N个整数表示每棵树的高度。
.
输出格式: 1个整数,表示锯片的最高高度。
样例1
输入:
4 7
20 15 10 17
.
输出: 15
(2)参考代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
LL n,m;
LL a[1000010];
//判断h是否满足要求
bool check(int h)
{
LL sum=0;
for(LL i=0;i<n;i++)
{
if(a[i]>h) sum+=(a[i]-h);
}
if(sum>=m) return true;
else return false;
}
int main()
{
cin>>n>>m;
//求出树的高度最大值作为二分的右端点
LL r=0;
for(LL i=0;i<n;i++)
{
scanf("%lld",&a[i]);
r=max(r,a[i]);
}
LL l=0;
while(l<r) //二分板子
{
LL mid=l+r+1>>1;
if(check(mid)) l=mid;
else r=mid-1;
}
cout<<l<<endl;
return 0;
}
结语
感谢大家一直以来的不断支持与鼓励,码题集题库中的进阶塔350题正在逐步更新,之后会逐步跟进星耀,王者的题,尽请期待!!!
同时,也希望这些题能帮助到大家,一起进步,祝愿每一个算法道路上的“苦行僧”们,都能够历经磨难,终成正果,既然选择了这条路,走到了这里,中途放弃,岂不是太过可惜?
另附中国计算机学会的杰出会员、常务理事轩哥博士的B站视频讲解链接https://space.bilibili.com/518554541/?spm_id_from=333.999.0.0,供大家更好的进行学习与刷题~( ̄▽ ̄~)~
愿你的结局,配得上你一路的颠沛流离。