24暑假集訓day1上午

Yantai_YZY發表於2024-08-03

上午

內容:列舉遞推貪心

廣告:推薦題單

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 插入排序

問題簡述:簡述不了

遇到問題可以從資料範圍入手 (我記得有個 \(\text{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;
}

記錄

\(\text{Tip}\)array\(\text{C++11}\) 的東西。本程式碼中, array<int, 2> b[xx]; 宣告瞭一個大小為 xxarray 陣列,每個元素都是一個包含兩個整數的陣列。 好處是可以透過 b[i][j] 的方式來訪問陣列元素。


貪心

憑藉大致的感覺 (猜)

我簡單畫了一下

T3 紀念品分組

問題簡述:有 \(n\) 個人,第 \(i\) 個人的重量是 \(W_i\)。每艘船的最大載重均為 \(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 過河卒

問題簡述:如圖

思路在 \(\text{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;
}

記錄

賽後補的,當時洛谷炸了