2020年牛客算法入门课练习赛1

第k小数
链接:https://ac.nowcoder.com/acm/contest/12144/A
来源:牛客网

题目描述
给你一个长度为n的序列,求序列中第k小数的多少。
输入描述:
多组输入,第一行读入一个整数T表示有T组数据。
每组数据占两行,第一行为两个整数n,k,表示数列长度和k。
第二行为n个用空格隔开的整数。
输出描述:
对于每组数据,输出它的第k小数是多少。
每组数据之间用空格隔开
示例1
输入
复制
2
5 2
1 4 2 3 4
3 3
3 2 1
输出
复制
2
3
备注:
t \leq10 , 1\leq n\leq5\times 10^6,k\leq n,数列里每个数都在int范围内t≤10,1≤n≤5×10
6
,k≤n,数列里每个数都在int范围内
由于输入比较多,请使用快读读取。
例如:
inline int read(){
int x = 0, f = 1;
char ch = getchar();
while(ch < ‘0’ || ch > ‘9’){
if (ch == ‘-’)
f = -1;
ch = getchar();
}
while(ch >= ‘0’ && ch <= ‘9’){
x = (x<<1) + (x<<3) + (ch^48);
ch = getchar();
}
return x * f;
}

使用快速的读入方式

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>
#include <string>
#include <queue>
#include <map>
#include <stack>
#include <map>
#include <unordered_map>
#include <vector>
#include <cmath>
//#include <ext/rope>
#include <bits/stdc++.h> 

using namespace std;
using namespace __gnu_cxx;

#define gt(x) x = read()
#define int long long
#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define endl "\n"
//#define x first
//#define y second

int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0}; 

//typedef __int128 INT;
typedef pair<double, int> PDI;
typedef pair<int, int> PII;
typedef unsigned long long ULL;


inline int read(){
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9'){
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9'){
        x = (x<<1) + (x<<3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

const int N = 5e6 + 10;
const int M = N * 10;
const long long mod = 998244353;
const int PP = 131;
const int inf = 0x3f3f3f3f;
const int INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-10;
const double PI = acos(-1);

int a[N];

signed main(){
	int T;
	while(~scanf("%lld", &T)){
		while(T --){
			int n, k;
			gt(n);
			gt(k);
			
			for (int i = 1; i <= n; i ++){
				gt(a[i]);
			}
			
			sort(a + 1, a + 1 + n);
			
			printf("%lld\n", a[k]);
		}
	}
	
	return 0;
}

不平行的直线
链接:https://ac.nowcoder.com/acm/contest/12144/B
来源:牛客网

题目描述
在坐标纸上有N个不重合的点,两两可以连一个线段并延伸成直线,请问在这些直线里最多能选出多少条使得他们两两不平行也不重合。

输入描述:
第1行: 输入1个正整数:N

第2…N+1行:第i+1行是两个用空格隔开的整数,为点i的坐标(Xi,Yi)

输出描述:
输出1个整数,为最多的互不平行的直线数目。
示例1
输入
复制
3
1 0
-2 0
0 0
输出
复制
1
备注:
N \leq 200,-1000 \leq X_i,Y_i \leq 1000N≤200,−1000≤X
i

,Y
i

≤1000

数据范围非常小,枚举每两个边即可,需要注意的是如果两个点的横坐标相等,那么他们的斜率是不存在的,需要特判

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>
#include <string>
#include <queue>
#include <map>
#include <stack>
#include <map>
#include <unordered_map>
#include <vector>
#include <cmath>
//#include <ext/rope>
#include <bits/stdc++.h> 

using namespace std;
using namespace __gnu_cxx;

#define gt(x) x = read()
#define int long long
#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define endl "\n"
//#define x first
//#define y second

int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0}; 

//typedef __int128 INT;
typedef pair<double, int> PDI;
typedef pair<int, int> PII;
typedef unsigned long long ULL;

inline int read(){
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9'){
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9'){
        x = (x<<1) + (x<<3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

const int N = 210+ 10;
const int M = N * 10;
const long long mod = 998244353;
const int PP = 131;
const int inf = 0x3f3f3f3f;
const int INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-10;
const double PI = acos(-1);

int x[N], y[N];
int n;
set<double> ss;

double calc(int i, int j){
	int x1 = x[i], y1 = y[i];
	int x2 = x[j], y2 = y[j];
	
	if (x2 == x1){
		return PI;
	}
	return (double)(y2 * 1.0 - y1 * 1.0) / (double)(x2 * 1.0 - x1 * 1.0);
}

signed main(){
	gt(n);
	for (int i = 1; i <= n; i ++){
		gt(x[i]), gt(y[i]);
	}
	
	int res = 0;
	for (int i = 1; i <= n; i ++){
		for (int j = i + 1; j <= n; j ++){
			//cout << i << " " << j << " " << calc(i, j) << endl;
			if (!ss.count(calc(i, j))){
				ss.insert(calc(i, j));
				res ++;
			}
		}
	}
	
	cout << res << endl;
	
	return 0;
}

丢手绢

链接:https://ac.nowcoder.com/acm/contest/12144/C
来源:牛客网

题目描述
“丢丢手绢,轻轻地放在小朋友的后面,大家不要告诉她,快点快点抓住她,快点快点抓住她。”
牛客幼儿园的小朋友们围成了一个圆圈准备玩丢手绢的游戏,但是小朋友们太小了,不能围成一个均匀的圆圈,即每个小朋友的间隔可能会不一致。为了大家能够愉快的玩耍,我们需要知道离得最远的两个小朋友离得有多远(如果太远的话牛老师就要来帮忙调整队形啦!)。
因为是玩丢手绢,所以小朋友只能沿着圆圈外围跑,所以我们定义两个小朋友的距离为沿着圆圈顺时针走或者逆时针走的最近距离。
输入描述:
第一行一个整数N,表示有N个小朋友玩丢手绢的游戏。
接下来的第2到第n行,第i行有一个整数,表示第i-1个小朋友顺时针到第i个小朋友的距离。
最后一行是第N个小朋友顺时针到第一个小朋友的距离。
输出描述:
输出一个整数,为离得最远的两个小朋友的距离。
示例1
输入
复制
3
1
2
3
输出
复制
3
备注:
2 \leq N \leq 1000002≤N≤100000
距离和(圆圈周长)小于等于2147483647

如何分析这个题,两个小朋友的距离为他们比较近的那个距离,如何让这个距离最大,那么需要两个点的两个距离是最接近的,首先我们需要把环变成链,然后尺取出最小的大于等于一半的长度,分析是上取整,然后和减去这个长度就是答案,很明显还可以使用二分,逆向思维,而分出小于等于一半的最大值,这里是下取整,需要注意的一个点是枚举左端点,需要二分的是右端点,但是有可能第一个距离就已经大于一半了,所以右区间的左端点是枚举的左端点本身

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>
#include <string>
#include <queue>
#include <map>
#include <stack>
#include <map>
#include <unordered_map>
#include <vector>
#include <cmath>
//#include <ext/rope>
#include <bits/stdc++.h> 

using namespace std;
using namespace __gnu_cxx;

#define gt(x) x = read()
#define int long long
#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define endl "\n"
//#define x first
//#define y second

int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0}; 

//typedef __int128 INT;
typedef pair<double, int> PDI;
typedef pair<int, int> PII;
typedef unsigned long long ULL;

inline int read(){
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9'){
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9'){
        x = (x<<1) + (x<<3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

inline void print(int x) {
  if (x < 0) { putchar('-'); x = -x; }
  if (x >= 10) print(x / 10);
  putchar(x % 10 + '0');
}

const int N = 1e5 + 10;
const int M = N * 10;
const long long mod = 998244353;
const int PP = 131;
const int inf = 0x3f3f3f3f;
const int INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-10;
const double PI = acos(-1);

int a[N * 2];

signed main(){
	int n;
	gt(n);
	
	int sum = 0;
	for (int i = 1; i <= n; i ++){
		gt(a[i]);
		a[i + n] = a[i];
		sum += a[i];
	}
	
	n *= 2;
	int res = (sum + 1) / 2;
	int ans = sum;
	
	int j = 1;
	int temp = 0;
//	cout << res << "----" << sum << endl;
	for (int i = 1; i <= n; i ++){
		if (temp >= res)   ans = min(ans, temp);
		while(j <= n && temp < res){
			temp += a[j];
			j ++;
			if (temp >= res){
				ans = min(ans, temp);
				break;
			}
		
		}
		
		temp -= a[i];
	}
	
	//cout << ans << endl;
	cout << sum - ans << endl;
	
	return 0;
}

#include<iostream>
#define int long long

using namespace std;
const int N=2e5+10;

int a[N],s[N];
int n;

signed main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
        s[i]=s[i-1]+a[i];
    }
    for(int i=1;i<=n;i++) s[n+i]=s[n+i-1]+a[i];
    int maxv=0;
    for(int i=0;i<=n;i++) 
    {
        int l=i,r=i+n;
        while(l<r)
        {
            int mid=l+r+1>>1;
            if(s[mid]-s[i]>s[n]/2) r=mid-1;
            else l=mid;
        }
        int t=s[r]-s[i];
        maxv=max(maxv,t);
    }
    cout<< maxv <<endl;
    return 0;
}

二分
链接:https://ac.nowcoder.com/acm/contest/12144/D
来源:牛客网

题目描述
我们刚刚学了二分查找——所谓二分查找就是在一堆有序数里找某个符合要求的数。在学完二分查找之后如果让你玩猜数游戏(裁判选定一个目标数字,你说一个数裁判告诉你是高了还是低了直到你猜到那个数)的话,显然你会用二分的方式去猜。
但是不是每一个玩猜数游戏的人都知道二分是最好,甚至一个健忘的玩家都有可能在得到裁判回答的下一个瞬间就忘了他之前问了什么以及裁判的回答),而现在更可怕的是,这个告诉你猜的数是高还是低的裁判他也很健忘,他总是薛定谔的记得这个目标数字,也就是说他的回答有可能出错。我们已经不关心这个不靠谱的游戏本身了,我们更关心裁判这个薛定谔的记得到底有几个是记得…
现在给出这个健忘的玩家的所有猜测和裁判的所有回答,问裁判最多能有多少次是记得目标数字的,即裁判的回复是符合情况的。
输入描述:
第一行包含一个正整数n,表示裁判的回答数(也是玩家的猜数次数)。
接下来n行,首先是猜的数,然后是一个空格,然后是一个符号。符号如果是“+”说明猜的数比答案大,“-”说明比答案小,“.”说明猜到了答案。
输出描述:
包含一个正整数,为裁判最多有多少个回答是正确的。
示例1
输入
复制
4
5 .
8 +
5 .
8 -
输出
复制
3
说明
当目标数组是5时,5 . 5 . 8 + 这三个回答都是正确的
备注:
n \leq 100000n≤100000
所有数的大小都小于int类型最大值。

如何分析这个题,尝试了二分发现不能发现单调性,然后就想着暴力枚举,枚举的话我们要枚举的是数据范围比较小的,正常的思路是枚举没每个数满足几个条件,但是数据范围实在太大,所以我们需要逆向思维,我们枚举每个条件,这个条件可以满足哪些数,然后让哪些数满足的条件个数都加1,可以差分处理,最后前缀和就可以求出

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>
#include <string>
#include <queue>
#include <map>
#include <stack>
#include <map>
#include <unordered_map>
#include <vector>
#include <cmath>
//#include <ext/rope>
#include <bits/stdc++.h> 

using namespace std;
using namespace __gnu_cxx;

#define gt(x) x = read()
#define int long long
#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define endl "\n"
//#define x first
//#define y second

int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0}; 

//typedef __int128 INT;
typedef pair<double, int> PDI;
typedef pair<int, int> PII;
typedef unsigned long long ULL;

inline int read(){
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9'){
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9'){
        x = (x<<1) + (x<<3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

inline void print(int x) {
  if (x < 0) { putchar('-'); x = -x; }
  if (x >= 10) print(x / 10);
  putchar(x % 10 + '0');
}

const int N = 1e5 + 10;
const int M = N * 10;
const long long mod = 998244353;
const int PP = 131;
const int inf = 0x3f3f3f3f;
const int INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-10;
const double PI = acos(-1);

int a[N];
char s[N];
map<int, int> mp;

signed main(){
	ios;
	int n;
	cin >> n;
	for (int i = 1; i <= n; i ++){
		cin >> a[i] >> s[i];
	}
	
	for (int i = 1; i <= n; i ++){
		if (s[i] == '.'){
			mp[a[i]] ++;
			mp[a[i] + 1] --;
		}
		if (s[i] == '+'){
			mp[a[i]] --;
			mp[-inf] ++;
		}
		if (s[i] == '-'){
			mp[a[i] + 1] ++;
		}
	}
	
	int ans = -inf;
	int h = 0;

	for (auto it = mp.begin(); it != mp.end(); it ++){
		h += it -> second;
		ans = max(ans, h);
	}
	
	cout << ans << endl;
	
	return 0;
}

交换
链接:https://ac.nowcoder.com/acm/contest/12144/E
来源:牛客网

题目描述
牛客幼儿园的小朋友课间操时间需要按照学号从小到大排队,但是他们太小了只能站成一列顺序却不对,现在幼儿园的阿姨需要帮忙交换小朋友的位置让他们最终有序,阿姨希望能尽快完成交换操作,问最少需要交换多少次,才能使得小朋友们从小到大排好。
注意:每个小朋友的学号不同,但是未必连续,因为可能有小朋友请假了没有来。
输入描述:
第一行一个整数 N。
接下来 N 行每行一个整数,为小朋友们的队列。
输出描述:
一个整数表示小朋友们的最小交换次数。
示例1
输入
复制
3
2
1
3
输出
复制
1
备注:
N\leq100000,其他整数均\leq 10^{9}N≤100000,其他整数均≤10
9

一开始按照逆序对写,大意了,然后这个题应该如何思考,我每次找到最小的数,然后看他是否在他对应的位置上,如果不在的话,就和对应位置的那个数交换,交换的话需要交换的有数本身,还有每个数对应的位置,模拟即可

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>
#include <string>
#include <queue>
#include <map>
#include <stack>
#include <map>
#include <unordered_map>
#include <vector>
#include <cmath>
//#include <ext/rope>
#include <bits/stdc++.h> 

using namespace std;
using namespace __gnu_cxx;

#define gt(x) x = read()
#define int long long
#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define endl "\n"
//#define x first
//#define y second

int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0}; 

//typedef __int128 INT;
typedef pair<double, int> PDI;
typedef pair<int, int> PII;
typedef unsigned long long ULL;

inline int read(){
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9'){
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9'){
        x = (x<<1) + (x<<3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

inline void print(int x) {
  if (x < 0) { putchar('-'); x = -x; }
  if (x >= 10) print(x / 10);
  putchar(x % 10 + '0');
}

const int N = 1e6 + 10;
const int M = N * 10;
const long long mod = 998244353;
const int PP = 131;
const int inf = 0x3f3f3f3f;
const int INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-10;
const double PI = acos(-1);

int a[N];
map<int, int> mp;

signed main(){
	priority_queue<int, vector<int>, greater<int>> heap;
	int n;
	scanf("%lld", &n);
	for (int i = 1; i <= n; i ++){
		scanf("%lld", &a[i]);
		heap.push(a[i]);
		mp[a[i]] = i;
	}
	
	int idx = 1;
	int res = 0;
	while(heap.size()){
		auto t = heap.top();
		heap.pop();
		
		if (mp[t] != idx){
			res ++;
			//int temp1 = mp[a[idx]];
			//int temp2 = a[mp[t]];
			mp[a[idx]] = mp[t];
			a[mp[t]] = a[idx];
			a[idx] = t;
			mp[t] = idx;
		}
		
		idx ++;
	}
	
	print(res);
	
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值