上午
内容:枚举递推贪心
广告:推荐此题单
1. 枚举
枚举:最基础、最容易想到
本质:不重复,不遗漏
问题简述:将整数 \(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;
}
问题简述:简述不了
遇到问题可以从数据范围入手 (我记得有个 \(\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]
的方式来访问数组元素。
贪心
凭借大致的感觉 (猜)
我简单画了一下
问题简述:有 \(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;
}
思路:能闪则闪,闪烁在能闪时一定比跑的快;分批进行,判断哪个更快。
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. 递推
本质;在记录一些状态,然后根据之前的状态推出下一个状态。就是在记录信息,并尝试推出剩下信息的过程。
问题简述:如图
思路在 \(\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;
}
赛后补的,当时洛谷炸了
标签:24,std,int,s1,day1,xx,using,include,集训 From: https://www.cnblogs.com/yantaiyzy2024/p/18340805