牛客算法竞赛入门课第一节习题

牛客算法竞赛入门课第一节习题


数学考试

题目描述

今天qwb要参加一个数学考试,这套试卷一共有n道题,每道题qwb能获得的分数为ai,qwb并不打算把这些题全做完, 他想选总共2k道题来做,并且期望他能获得的分数尽可能的大,他准备选2个不连续的长度为k的区间, 即[L,L+1,L+2,....,L+k-1],[R,R+1,R+2,...,R+k-1](R >= L+k)。

输入描述:

第一行一个整数T(T<=10),代表有T组数据接下来一行两个整数n,k,(1<=n<=200,000),(1<=k,2k <= n)接下来一行n个整数a1,a2,...,an,(-100,000<=ai<=100,000)

输出描述:

输出一个整数,qwb能获得的最大分数

输入

2
6 3
1 1 1 1 1 1
8 2
-1 0 2 -1 -1 2 3 -1

输出

6
7

题目大意

n道题,不做完,做2k道,连续两个不重合长度为k的区间,分数最大

思路

dp+前缀和
1.先求前缀和
2.从前到后求出数组下标i前的最大k长度区间和,不断更新(前一个的最大和最当前的和进行比较)
3从后到前求出数组下标i后的最大k长度区间和,不断更新(后一个的最大和最当前的和进行比较)
4.枚举所有i的前后最大值的和,找出最大和

错误思路

先求最大的k长度区间和,再从两边找第二大的k长度区间和
错误原因:情况考虑不全。

/*#include<bits/stdc++.h>错误思路
using namespace std;
int num[2000005];
struct date{
	int a;
	int head;
	int wei;
}sum[2000005];
bool cmp(date a,date b)
{
	return a.a>b.a;
}
int main()
{
	//n道题 每道ai分 不全部做完,做2k道题,分数尽可能大
	//前缀和
	//不重合的两个区间 
	int t;
	scanf("%d",&t);
	while(t--)
	{
		for(int i=0;i<2000005;i++)//初始化 
		{
			num[i]=0;
			sum[i].a=0;
			sum[i].head=0;
			sum[i].wei=0;
		}
		int n=0,k=0;
		scanf("%d%d",&n,&k);
		for(int i=0;i<n;i++)
		{
			scanf("%d",&num[i]);
		}
		for(int i=0;i<k;i++)
		{
			sum[0].a=sum[0].a+num[i];
		}
		sum[0].head=0;
		sum[0].wei=k-1;
		//printf("sum[0]=%d\n",sum[0]); 
		for(int i=1;i<n&&sum[i].wei<n;i++)
		{
			sum[i].a=sum[i-1].a+num[sum[i-1].wei+1]-num[sum[i-1].head];
			sum[i].head=sum[i-1].head+1;
			sum[i].wei=sum[i-1].wei+1;
		}
		sort(sum,sum+n,cmp);
		int max=sum[0].a;
		sum[0].a=0;
		int maxn=0;
		for(int i=0;i<n;i++)
		{
			if(sum[i].a>maxn)
			{
				if(sum[i].head>sum[0].wei||sum[i].wei<sum[0].head)
				{
					maxn=sum[i].a;
				}
			}
		}
		printf("%d\n",max+maxn);
	 } 
	return 0;
 } */
#include<bits/stdc++.h>
using namespace std;
long long int sum[200005];
long long int f[200005];
long long int s[200005];
int main()
{
	//dp记录下标i前最大的值,i后最大的值 
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int n,k;
		long long ans=-1e18;
		scanf("%d%d",&n,&k);
		memset(f,-0x7f,sizeof(f)) ;
        memset(s,-0x7f,sizeof(s));
		for(int i=1;i<=n;i++)
		{
			scanf("%lld",&sum[i]);
			sum[i]=sum[i]+sum[i-1];
		}
		for(int i=k;i<=n-k;i++)
		{
			f[i]=max(f[i-1],sum[i]-sum[i-k]);
		}
		for(int i=n-k+1;i>=k+1;i--)
		{
			s[i]=max(s[i+1],sum[i+k-1]-sum[i-1]);
		}
		for(int i=k;i<=n-k;i++)
		{
			ans=max(ans,f[i]+s[i+1]);
		}
		printf("%lld\n",ans);
	}
	return 0;
}

纪念品分组

题目描述

元旦快到了,校学生会让乐乐负责新年晚会的纪念品发放工作。为使得参加晚会的同学所获得 的纪念品价值相对均衡,他要把购来的纪念品根据价格进行分组,但每组最多只能包括两件纪念品, 并且每组纪念品的价格之和不能超过一个给定的整数。为了保证在尽量短的时间内发完所有纪念品,乐乐希望分组的数目最少。 你的任务是写一个程序,找出所有分组方案中分组数最少的一种,输出最少的分组数目。

输入描述:

第 1 行包括一个整数 w,为每组纪念品价格之和的上限。第 2 行为一个整数n,表示购来的纪念品的总件数。第 3 ~ n+2 行每行包含一个正整数 pi ( 5 ≤ pi ≤ w ) ,表示所对应纪念品的价格。

输出描述:

包含一个整数,即最少的分组数目。

输入

100
9
90
20
20
30
50
60
70
80
90

输出

6

题目大意

分组,每组最多两件,每组和不能超过w,组数最少

思路

水题,排序后枚举即可,注意:每两个人符合条件,组数-1,不是-2。

#include<bits/stdc++.h>
using namespace std;
int num[30005]; 
int main()
{
	//分组,每组最多两件,加个和不能超过w,组数最少
	//贪心 
	int w;
	scanf("%d",&w);
	int n;
	scanf("%d",&n);
	for(int i=0;i<n;i++)
	{
		scanf("%d",&num[i]);
	}
	sort(num,num+n);
	int head=0,fail=n-1;
	int sum=n;
	for(int i=0;i<n;i++)
	{
		//printf("%d ",num[i]);
		if(num[head]+num[fail]<=w&&head<fail)
		{
			sum--;
			fail--;
			head++; 
		}else{
			fail--;
		}
	}
	 printf("%d",sum);
	return 0;
}

校门外的树

题目描述

某校大门外长度为L的马路上有一排树,每两棵相邻的树之间的间隔都是1米。我们可以把马路看成一个数轴,马路的一端在数轴0的位置,另一端在L的位置;数轴上的每个整数点,即0,1,2,……,L,都种有一棵树。 由于马路上有一些区域要用来建地铁。这些区域用它们在数轴上的起始点和终止点表示。已知任一区域的起始点和终止点的坐标都是整数,区域之间可能有重合的部分。现在要把这些区域中的树(包括区域端点处的两棵树)移走。你的任务是计算将这些树都移走后,马路上还有多少棵树。

输入描述:

第一行有两个整数:L(1 <= L <= 10000)和 M(1 <= M <= 100),L代表马路的长度,M代表区域的数目,L和M之间用一个空格隔开。接下来的M行每行包含两个不同的整数,用一个空格隔开,表示一个区域的起始点和终止点的坐标。

输出描述:

包括一行,这一行只包含一个整数,表示马路上剩余的树的数目。

输入

500 3
150 300
100 200
470 471

输出

298

题目大意

0-L都有树,一些可能重合的区间要拔树,求最后更多少树

思路

统计+枚举
1.先将区间内的枚举=1,
2.统计多少个1
3.总数-统计

扩展

此题数据范围并不大
在数据范围很大的时候,考虑使用离散化解题

#include<bits/stdc++.h> 
using namespace std;
int main()
{
	int l,m,sum=0;
	scanf("%d%d",&l,&m);
	int num[l]={0};
	for(int i=0;i<m;i++)
	{
		int a=0,b=0;
		scanf("%d%d",&a,&b);
		for(int j=a;j<=b;j++)
		{
			num[j]=1;
		}
	}
	for(int i=0;i<l;i++)
	{
		if(num[i]==1)
		{
			sum++;
		}
	}
	printf("%d",l-sum+1);
}

明明的随机数

题目描述

明明想在学校中请一些同学一起做一项问卷调查,为了实验的客观性,他先用计算机生成了N个1到1000之间的随机整数(N ≤ 100),对于其中重复的数字,只保留一个,把其余相同的数去掉,不同的数对应着不同的学生的学号。然后再把这些数从小到大排序,按照排好的顺序去找同学做调查。请你协助明明完成“去重”与“排序”的工作。

输入描述:

输入有2行,第1行为1个正整数,表示所生成的随机数的个数:N第2行有N个用空格隔开的正整数,为所产生的随机数。

输出描述:

输出2行,第1行为1个正整数M,表示不相同的随机数的个数。第2行为M个用空格隔开的正整数,为从小到大排好序的不相同的随机数。

输入

10
20 40 32 67 40 20 89 300 400 15

输出

8
15 20 32 40 67 89 300 400

题目大意

1-1000个随机数 去重 从小到大输出

思路

水题 去重+排序
代码中思路为:统计

#include<bits/stdc++.h>
using namespace std;
int num[1005];
int main()
{
	int n;
	scanf("%d",&n);
	int a=0,sum=0;
	for(int i=0;i<n;i++)
	{
		scanf("%d",&a);
		num[a]++;
	}
	for(int i=0;i<1005;i++)
	{
		if(num[i]>=1)
		{
			sum++;
		}
	}
	printf("%d\n",sum);
	for(int i=0;i<1005;i++)
	{
		if(num[i]>=1)
		{
			printf("%d ",i);
		}
	}
	return 0;
}

拼数

题目描述

设有n个正整数(n ≤20),将它们联接成一排,组成一个最大的多位整数。 例如:n=3时,3个整数13,312,343联接成的最大整数为:34331213 又如:n=4时,4个整数7,13,4,246联接成的最大整数为:7424613

输入描述:

第一行,一个正整数n。第二行,n个正整数。

输出描述:

一个正整数,表示最大的整数

输入

3
13 312 343

输出

34331213

题目大意

n个数拼凑成一个数,求最大

思路

把数转化为字符串,位数高的越大越好 利用string将所有字符串按字典序排序(a+b>b+a)

#include<bits/stdc++.h>
using namespace std;
//自定义排序函数
bool cmp(string a,string b)
{
    return a+b>b+a;//如果a+b>b+a,则把a排在前面,否则将b排在前面
}
int main()
{
    int n;
    cin>>n;
    string s[n];
    //获取n个正整数,存入字符串数组a中
    for (int i = 0; i < n; ++i)
    {
        cin>>s[i];
    }
    sort(s,s+n,cmp);
    for (int i = 0; i < n; ++i)
    {
        cout<<s[i];
    }
    return 0;
}

值周

题目描述

JC内长度为L的马路上有一些值周同学,每两个相邻的同学之间的间隔都是1米。我们可以把马路看成一个数轴,马路的一端在数轴0的位置,另一端在L的位置;数轴上的每个整数点,即0,1,2,…L,都有一个值周同学。 由于水宝宝有用一些区间来和ssy搞事情,所以为了避免这种事走漏风声,水宝宝要踹走一些区域的人。这些区域用它们在数轴上的起始点和终止点表示。已知任一区域的起始点和终止点的坐标都是整数,区域之间可能有重合的部分。现在要把这些区域中的人(包括区域端点处的两个人)赶走。你的任务是计算将这些人都赶走后,马路上还有多少个人。

输入描述:

第一行有2个整数L和M,L代表马路的长度,M代表区域的数目,L和M之间用一个空格隔开。 接下来的M行每行包含2个不同的整数,用一个空格隔开,表示一个区域的起始点和终止点的坐标

输出描述:

1个整数,表示马路上剩余的人的数目。

输入

500 3
150 300
100 200
470 471

输出

298

题目大意

和校门外的树差不多

思路

由于数据很大,暴力会超时,需要使用离散化,按左端点进行排序
只记录头尾,给重合的头尾进行补充,维护成最后互不重合的几个区间,最后进行减去。

#include<bits/stdc++.h>
using namespace std;
struct date{
	long long int l=0;
	long long int r=0;
}num[1000005];
int ans[10000];
int aa=0;
bool cmp(date a,date b){
	return a.l<b.l;
}
int main()
{
	//每两个相邻同学间隔1米
	//踹走一些区域的人 
	//这些区域用它们在数轴上的起始点和终止点表示。
	//区域之间可能有重合的部分
	//(包括区域端点处的两个人)赶走
	//马路上还有多少个人。  
	long long int L,n;
	scanf("%lld%lld",&L,&n);
	long long int sum=L+1;
	for(int i=0;i<n;i++)
	{
		scanf("%lld%lld",&num[i].l,&num[i].r);
	}
	sort(num,num+n,cmp);
	long long int head=num[0].l;
	long long int fail=num[0].r;
	for(int i=1;i<n;i++)
	{
		if(num[i].l<=fail)
		{
			fail=max(fail,num[i].r);
		}
		else
		{
			ans[aa]=fail-head+1;
			head=num[i].l;
			fail=num[i].r;
			aa++;
		}
	}
	ans[aa]=fail-head+1;
	for(int i=0;i<=aa;i++)
	{
		//printf("%d %d\n",num[i].l,num[i].r);
		//printf("%d\n",ans[i]);
		sum=sum-ans[i];
	}
	printf("%d",sum);
	return 0;
 } 
 

Selfish Grazing

题目描述

Each of Farmer John's N (1 <= N <= 50,000) cows likes to graze in a certain part of the pasture, which can be thought of as a large one-dimeensional number line. Cow i's favorite grazing range starts at location Si and ends at location Ei (1 <= Si < Ei; Si < Ei <= 100,000,000). Most folks know the cows are quite selfish; no cow wants to share any of its grazing area with another. Thus, two cows i and j can only graze at the same time if either Si >= Ej or Ei <= Sj. FJ would like to know the maximum number of cows that can graze at the same time for a given set of cows and their preferences. Consider a set of 5 cows with ranges shown below:
... 1 2 3 4 5 6 7 8 9 10 11 12 13 ...
... |----|----|----|----|----|----|----|----|----|----|----|----|----
Cow 1: <=:=> : : :
Cow 2: <:::=>:
Cow 3: : <
> : : :
Cow 4: : : <====:=> :
Cow 5: : : <
> : :

These ranges represent (2, 4), (1, 12), (4, 5), (7, 10), and (7, 8), respectively.
For a solution, the first, third, and fourth (or fifth) cows can all graze at the same time. If the second cow grazed, no other cows could graze. Also, the fourth and fifth cows cannot graze together, so it is impossible for four or more cows to graze.

输入描述:

  • Line 1: A single integer: N* Lines 2..N+1: Line i+1 contains the two space-separated integers: Si and Ei

输出描述:

  • Line 1: A single integer representing the maximum number of cows that can graze at once.

输入

5
2 4
1 12
4 5
7 10
7 8

输出

3

题目大意

场有N头牛,每头牛都有它喜欢的放牧区间[si,Ei]没有牛愿意与其他人共享任何放牧区域。因此,如果Si> = Ej或Ei <= Sj,则两只母牛i和j只能同时放牧。FJ希望知道给定的一组奶牛可以同时放牧的最大奶牛数量及其偏好。

思路

贪心 维护区间,按右端点进行排序,如果坐端点大于右端点(代表不重合) 则更新新的右端点。

#include<bits/stdc++.h>
using namespace std;
struct date{
	int l=0;
	int r=0;
}cow[50005];
bool cmp(date a,date b)
{
	return a.r<b.r;
}
int main()
{
	//牧场有N头牛,每头牛都有它喜欢的放牧区间[si,Ei]
    //没有牛愿意与其他人共享任何放牧区域。
	//因此,如果Si> = Ej或Ei <= Sj,
	//则两只母牛i和j只能同时放牧。
	//FJ希望知道给定的一组奶牛可以同时放牧的最大奶牛数量及其偏好。
	int n,sum=0,right=0;
	scanf("%d",&n);
	for(int i=0;i<n;i++)
	{
		scanf("%d%d",&cow[i].l,&cow[i].r);
	}
	sort(cow,cow+n,cmp);
	right=cow[0].r;
	for(int i=1;i<n;i++)
	{
		if(cow[i].l>=right)
		{
			sum++;
			//printf("%d %d ",cow[i].l,right);
			right=cow[i].r;
		}
	}
	printf("%d",sum+1);
	return 0;
 } 

切长条

题目描述

给定如图所示的若干个长条。你可以在某一行的任意两个数之间作一条竖线,从而把这个长条切开,并可能切开其他长条。问至少要切几刀才能把每一根长条都切开。样例如图需要切两刀。 注意:输入文件每行的第一个数表示开始的位置,而第二个数表示长度。

输入描述:

Line 1: A single integer, N(2 <= N <= 32000)Lines 2..N+1: Each line contains two space-separated positive integers that describe a leash. The first is the location of the leash's stake; the second is the length of the leash.(1 <= length <= 1e7)

输出描述:

Line 1: A single integer that is the minimum number of cuts so that each leash is cut at least once.

输入

7
2 4
4 7
3 3
5 3
9 4
1 5
7 3

输出

2

题目大意

若干长条,最少多少刀,全部切开

思路

按左端点排序 不断更新最小右端点,如果左端点大于等于最小右端点,则不重合,需要切一刀,最后要加上一刀

#include<bits/stdc++.h>
using namespace std;
struct date{
 int l=0;
 int r=0;
}F[32005];
bool cmp(date a,date b){
	return a.l<b.l;
}
int main()
{
	//任意两个数之间做竖线,切开长条,至少几刀都切开
	//当起点,大于等于排序后前一个点的时候(最小值),给一刀 
	int n,sum=0;
	scanf("%d",&n);
	for(int i=0;i<n;i++)
	{
		scanf("%d%d",&F[i].l,&F[i].r);
		F[i].r=F[i].l+F[i].r;
	}
	sort(F,F+n,cmp);
	int minn=F[0].r;
	for(int i=1;i<n;i++)
	{
		minn=min(minn,F[i].r);
		//更新最新最小右端点 
		//printf("前r和现r的最小值=%d ",minn); 
        if(F[i].l>=minn) sum++,minn=F[i].r;//判断并更新结束点最小值
		//printf("l=%d r=%d min2=%d\n",F[i].l,F[i].r,minn);
	 }
	printf("%d",sum+1);
	return 0;
 } 

Subsequence

题目描述

A sequence of N positive integers (10 < N < 100 000), each of them less than or equal 10000, and a positive integer S (S < 100 000 000) are given. Write a program to find the minimal length of the subsequence of consecutive elements of the sequence, the sum of which is greater than or equal to S.

输入描述:

The first line is the number of test cases. For each test case the program has to read the numbers N and S, separated by an interval, from the first line. The numbers of the sequence are given in the second line of the test case, separated by intervals. The input will finish with the end of file.

输出描述:

For each the case the program has to print the result on separate line of the output file.if no answer, print 0.

输入

2
10 15
5 1 3 5 10 7 4 9 2 8
5 11
1 2 3 4 5

输出

2
3

题目大意

求一个最小长度的区间满足条件

思路

前缀和+尺举法(维护一个区间(差分))不断更新最小长度

#include <iostream> 
#include <algorithm>
#include <math.h>
using namespace std; 
int num[100000005];
int main()
{
    int t;
	scanf("%d",&t);
	while(t--)
	{
		int n,sum;
        int minn=100000005;
		scanf("%d%d",&n,&sum);
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&num[i]);
			num[i]=num[i]+num[i-1];
		}
        int r=1;
        int l=1;
        while (r<=n)///边界条件
       {
           if (num[r]-num[l-1]>=sum)
           {
               minn=min((r-l+1),minn);///比较当前满足的子序列
               ++l;
           }
           else ++r;
       }
		if(minn==100000005)
		{
			printf("0\n");
		}else{
			printf("%d\n",minn);
		}
	}
	return 0;
 } 

------------恢复内容结束------------

posted @ 2020-05-24 20:29  Joker-Li  阅读(375)  评论(0)    收藏  举报