上午
內容:列舉遞推貪心
廣告:推薦此題單
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];
宣告瞭一個大小為 xx
的 array
陣列,每個元素都是一個包含兩個整數的陣列。 好處是可以透過 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;
}
記錄
賽後補的,當時洛谷炸了