24暑假集训day1下午

下午

内容:二分分治模拟

广告:推荐题单

1. 二分

二分查找

优点:在检验一个元素之后可以很快的舍弃掉一
半的元素,从而快速锁定目标元素。

T1 数的划分

问题简述:输入 n 个不超过 109 的单调不减的(就是后面的数字不小于前面的数字)非负整数 a1,a2,,an,然后进行 m 次询问。对于每次询问,给出一个整数 q,要求输出这个数字在序列中第一次出现的编号,如果没有找到的话输出 1

思路:
直接二分查找

std:

#include <iostream>
#define int long long
#define endl '\n'
using namespace std;
const int MAXN = 1e6 + 10;
int a[MAXN];
signed main(){
	int n, m;
	cin >> n >> m;
	for(int i = 1;i <= n;i++){
		cin >> a[i];
	}
	for(int i = 1;i <= m;i++){
		int x;
		cin >> x;
		int l = 1, r = n;
		while(l < r){
			int mid = (l + r) / 2;
			if(a[mid] >= x){
				r = mid;
			} else {
				l = mid + 1;
			}
		}
		if(a[l] != x){
			cout << "-1 ";
		} else {
			cout << l << " ";
		}
	}
	return 0;
}

记录

二分答案

用处:有最大值最小或者最小值最大的描述时可以尝试考虑二分答案 (反正你感觉很怪就对了)

T2 跳石头

问题简述:给你一排 N 块石头,你可以移走 M 块石头,使得最小的两块石头之间的距离尽可能长。

思路:
直接二分查找

std:

#include<bits/stdc++.h>

using namespace std;

int l , n , m;
int a[100010];
bool check(int d) {
	int last = 0 , cnt = 0;
	for(int i = 1 ; i <= n ; i ++) {
		if(a[i] - last < d) cnt ++;
		else last = a[i];
	}
	if(l - last < d) cnt ++;
	return cnt <= m;
}
int main() {
	scanf("%d%d%d", &l , &n , &m);
	for(int i = 1 ; i <= n ; i ++) {
		scanf("%d" , &a[i]);
	}
	int le = 1 , r = l + 1 , mid;
	while(le + 1 < r) {
		mid = (le + r) / 2;
		if(check(mid)) le = mid;
		else r = mid;
	}
	printf("%d", le);
	return 0;
}

记录


2. 分治

本质:分而治之

T3 跳石头

问题简述:组委会已经选择好了两块岩石作为比赛起点和终点。在起点和终点之间,有 N 块岩石(不含起点和终点的岩石)。在比赛过程中,选手们将从起点出发,每一步跳向相邻的岩石,直至到达终点。为了提高比赛难度,组委会计划移走一些岩石,使得选手们在比赛过程中的最短跳跃距离尽可能长。由于预算限制,组委会至多从起点和终点之间移走 M 块岩石(不能移走起点和终点的岩石)。

思路:

std:

#include <iostream>
using namespace std;
const int MAXN = 1e6 + 10;
int a[MAXN];
int L, n , m;
bool check(int d){
	int last = 0, cnt = 0;
	for(int i = 1;i <= n;i++){
		if(a[i] - last < d) cnt++;
		else last = a[i];
	}
	if(L - last < d){
		cnt++;
	}
	return cnt <= m;
}
int main(){
	scanf("%d%d%d", &L , &n , &m);
	for(int i = 1;i <= n;i++){
		scanf("%d", &a[i]);
	}
	int l = 1 , r = L + 1 , mid;
	while(l + 1 < r){
		mid = (l + r) / 2;
		if(check(mid)){
			l = mid;
		}
		else{
			r = mid;
		}
	}
	printf("%d", l);
	return 0;
}

记录


T4 逆序对

问题简述:对于给定的一段正整数序列,逆序对就是序列中 ai>aji<j 的有序对。知道这概念后,他们就比赛谁先算出给定的一段正整数序列中逆序对的数目。注意序列中可能有重复数字。

思路:

只要出现右侧的值<左侧的值的情况,就出现了逆序对。左侧还剩多少个值,就会出现多少个逆序对。

再把所有分治时出现的所有逆序对的个数加起来就行了

std:

#include<bits/stdc++.h> 
#define int long long
using namespace std;
const int MAXN = 500005;
int d[MAXN];
int a[MAXN],b[MAXN],c[MAXN];
int ans = 0;
void merge(int n,int m){
	int aa = 1,bb = 1;
	int cc = 0;
	while(aa <= n && bb <= m){
		if(a[aa] <= b[bb]){
			cc ++;
			c[cc] = a[aa];
			ans += bb - 1;
			aa ++;
		}
		else{
			cc ++;
			c[cc] = b[bb];
			bb ++;
		}
	}
	while(aa <= n){
		cc ++;
		c[cc] = a[aa];
		ans += bb - 1;
		aa ++;
	}
	while(bb <= m){
		cc ++;
		c[cc] = b[bb];
		bb ++;
	}
}
void guibing(int l,int r){
	if(l >= r){
		return;
	}
	int mid = (l+r)/2;
	guibing(l,mid);
	guibing(mid+1,r);
	for(int i=l;i<=mid;++i){
		a[i - l + 1] = d[i];
	}
	for(int i=mid+1;i<=r;++i){
		b[i - mid] = d[i];
	}
	merge(mid - l + 1,r - mid);
	for(int i=l;i<=r;++i)
		d[i] = c[i - l + 1];
}
signed main(){
	int n;
	scanf("%lld",&n);
	for(int i=1;i<=n;++i){
		scanf("%lld",&d[i]); 
	}
	guibing(1,n);
	printf("%lld",ans);
}

记录


3. 模拟

本质:按照题目的要求进行模拟操作

T5 铺地毯

std:

#include<iostream>
using namespace std;
int a[100000], b[100000], g[100000], k[100000];
int main(){
	int x, y, n ,s=-1;
	cin >> n;
	for(int i = 0;i < n;i++){
		cin >> a[i] >> b[i] >> g[i] >> k[i];
	}
	cin >> x >> y;
	for(int i = 0;i < n;i++){
	if(x >= a[i]&&y >= b[i]&&x <= a[i] + g[i]&&y <= b[i] + k[i]){
			s = i + 1;
		}
	}
	cout << s;
}

记录


T6 接水问题

std:

#include<cstdio>
using namespace std;
int w[10001], s[101], maxx;
int main(){
    int n, m;
    scanf("%d%d",&n,&m);
    for (int i = 1;i <= n;i++){
    	scanf("%d", &w[i]);
    }
    for (int i = 1;i <= n;i++){
        maxx = 1;
        for (int j = 2;j <= m;j++)
         if (s[maxx] > s[j]){
         		maxx = j;
         }
        s[maxx] += w[i];
    }
    maxx = 1;
    for (int i = 1;i <= m;i++){
    	maxx = s[i] > maxx ? s[i] : maxx;
    }
    printf("%d",maxx);
}

记录


T7 神奇的幻方

问题简述: 简述不了

std:

#include <bits/stdc++.h>
using namespace std;
int N, a[40][40], h, l;
int main(){
	cin >> N;
	a[0][N/2] = 1, h = 0, l = N / 2;
	for(int i = 2;i <= N * N;i++){
		if(h==0&&l!=N-1) a[N-1][l+1]=i,h=N-1,l++;
		else if(l == N - 1&&h != 0) a[h - 1][0] = i,h--, l = 0;
		else if(h == 0&&l == N - 1) a[h + 1][l] = i,h++;
		else if(h != 0&&l != N - 1){
			if(a[h - 1][l + 1] == 0&&h - 1 >= 0&&l + 1 <= N){
				a[h - 1][l + 1] = i;
				h--;
				l++;
			}
			else a[h + 1][l] = i, h++;
		}
	}
	for(int i = 0;i < N;i++){
		for(int j = 0;j<N;j++){
			cout << a[i][j] << ' ';
		}
		cout << endl;
	}
}

记录

posted @   Yantai_YZY  阅读(5)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示