24暑假集训day1上午

上午

内容:枚举递推贪心

广告:推荐题单

1. 枚举

枚举:最基础、最容易想到

本质:不重复,不遗漏

T1 数的划分

问题简述:将整数 n 分成 k 份,且每份不能为空,任意两个方案不相同(不考虑顺序)。

方法:搜索
关键:有顺序
具体步骤:
尝试从大到小进行拆分,我们记录当前数剩下总和,记录当前还需要拆分的剩下的个数,按照拆分数从大到小递归进行搜索。

std:

#include <iostream>
using namespace std;
#define int long long
int ans;
/*
x:当前剩余的整数数量
y:当前可以分配的整数的最大值
z:剩余的份数
*/
void dfs(int x, int y, int z){
	if(((x != 0) && (z == 0)) || (x == 0) && (z != 0)){
		return;
	}
	if(z == 0 && x == 0){
		return ++ans, void();
	}	
	if(x >= y){
		dfs(x - y, y, z - 1);
	}
	if(y > 1){
		dfs(x, y - 1, z);
	}
}
void solve(){
	int n, k;
	cin >> n >> k;
	dfs(n, n, k);
	cout << ans << endl;
	
}
signed main(){
	int __ = 1;
	while(__--){
		solve();
	}
	return 0;
}

记录


T2 插入排序

问题简述:简述不了

遇到问题可以从数据范围入手 (我记得有个 CSP 的题看数据范围可以判断你的方法的正误,不过我忘了)

至多存在 5000 次操作是 1 操作。

注意到修改操作很少,每次修改之后对原数组的影响不大

std:

#include <iostream>
#include <cmath>
#include <algorithm>
using namespace std;
const int xx = 2e5 + 5;
int a[xx], n, q;
array<int, 2>b[xx];
//也可以写成int b[xx][2];
int ans[xx]; 
int main(){
	scanf("%d %d", &n, &q);
	for(int i = 1;i <= n;i++){
		scanf("%d", &a[i]);
		b[i] = {a[i], i};
	}
	sort(b + 1, b + n + 1);
	for(int j = 1;j <= n;j++)
		ans[b[j][1]] = j;
	while(q--){
		int op;
		scanf("%d", &op);
		if(op == 1){
			int x, v;
			scanf("%d %d", &x, &v);
			a[x] = v,b[ans[x]] = {a[x], x};
			for(int j = ans[x];j >= 2;j--){
				if(b[j] < b[j - 1]){
					swap(b[j], b[j - 1]);
				}
			}
			for(int j = ans[x];j < n;j++){
				if(b[j + 1]<b[j]){
					swap(b[j + 1], b[j]);
				}
			}
			for(int j = 1;j <= n;j++){
				ans[b[j][1]] = j;
			}
		}
		else{
			int x;
			scanf("%d", &x);
			cout << ans[x] << '\n';
		}
	}
	return 0;
}

记录

TiparrayC++11 的东西。本代码中, array<int, 2> b[xx]; 声明了一个大小为 xxarray 数组,每个元素都是一个包含两个整数的数组。 好处是可以通过 b[i][j] 的方式来访问数组元素。


贪心

凭借大致的感觉 (猜)

我简单画了一下

T3 纪念品分组

问题简述:有 n 个人,第 i 个人的重量是 Wi。每艘船的最大载重均为 C,且最多容纳两个人,用最少的船装载所有人。

思路:尝试合并一个尽量大的组。

std:

#include <iostream>
#include <cmath>
#include <algorithm>
using namespace std;
const int MAXN=100005;
int a[MAXN];
int main(){
	int w, n;
	cin >> w >> n;
	for(int i = 1;i <= n;i++){
		cin >> a[i];
	}
	sort(a + 1, a + n + 1);
	int ans = 0;
	int i = 1, j = n;
	while(i <= j){
		if(a[i] + a[j] <= w){
			i++;
		}
		ans++;
		j--;
	}
	cout << ans;
	return 0;
}

记录


T4 守望者的逃离

思路:能闪则闪,闪烁在能闪时一定比跑的快;分批进行,判断哪个更快。

std:

#include <iostream> 
#define endl '\n'
using namespace std;
int main(){
	int m, s, t;
	cin >> m >> s >> t;
	int s1 = 0, s2 = 0;
 /*
s1:守望者在时间 t 内以固定速度 17m/s 移动的距离累计值。
s2:守望者在时间 t 内使用闪烁法术移动的距离累计值。
 */
	for(int i = 1;i <= t;i++){
		s1 += 17;
		if(m >= 10){
			s2 += 60;
			m -= 10;
		}
		else{
			m += 4;
		}
		if(s2 > s1){
			s1 = s2;
		}
		if(s1 > s){
          		cout << "Yes" << endl;
			cout << i << endl;
          		return 0;
		}
	}
	cout << "No" << endl;
	cout << s1 << endl;
	return 0;
}

记录


2. 递推

本质;在记录一些状态,然后根据之前的状态推出下一个状态。就是在记录信息,并尝试推出剩下信息的过程。

T5 过河卒

问题简述:如图

思路在 std 里,不想打了,其实就是找到递推规律并判断边界条件

std:

#include <bits/stdc++.h>
using namespace std;
bool f[105][105];
//存储马控点的方向 
int dx[]  =  {0, 2, 1, -1, -2, -2, -1, 1, 2};
int dy[]  =  {0, 1, 2, 2, 1, -1, -2, -2, -1}; 
long long dp[105][105];
int main(){
	int n, m, mx, my;
	cin >> n >> m >> mx >> my;
	for(int i = 0;i <= 8;i++){
		//ii和jj:马控点的坐标 
		int ii = mx+dx[i];
		int jj = my+dy[i];
		if(ii >= 0&&ii <= n&&jj >= 0&&jj <= m){
			f[ii][jj]  =  1;
		}
	}
	dp[0][0] = 1;//边界条件不能落下 
	for(int i = 0;i <= n;i++){
		for(int j = 0;j <= m;j++){
			if(i  ==  0&&j  ==  0){//dp[0][0]已经标记过 
				continue;	
			}
			if(f[i][j]  ==  0){//如果不被控制,有三种可能性,分别判断: 
				if(i  ==  0){
					dp[i][j] = dp[i][j-1];	
				}
				else if(j  ==  0){
					dp[i][j] = dp[i-1][j];	
				}
				else{
					dp[i][j] = dp[i-1][j]+dp[i][j-1];	
				}
			}
		}
	}
	cout << dp[n][m] << endl;
    return 0;
}

记录

赛后补的,当时洛谷炸了

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