0613ahut月赛题解 | JorbanS

文章提供了几道算法题目,包括不同类型的动态规划问题,如背包问题、修路专家问题以及字符串处理问题。对于每个问题,作者给出了代码实现,并讨论了复杂度和优化方法。同时,还涉及到了树形结构的处理,如小小园艺师问题。这些问题展示了在解决计算问题时如何运用动态规划和树形遍历策略。
摘要由CSDN通过智能技术生成

A 简单的背包

这题如果用传统01背包是将 f[i][j] 看作前 i 个物品在重量为 j 时的最大价值,复杂度极高,不可取

当初也是想让 f[i][j] 表示前 i 个物品在价值为 j 时的最小重量,复杂度达到了 1e7,故扔了这题 <= 纯🤡了

#include <iostream>
#include <cstring>

using namespace std;
typedef long long ll;
const int N = 1e3 + 2, V = 1e5 + 2;
ll n, weight;
ll w[N], v[N];
ll f[N][V];

int main() {
	memset(f, 0x3f, sizeof f);
	f[0][0] = 0;
	cin >> n >> weight;
	for (int i = 1; i <= n; i ++) cin >> w[i] >> v[i];
	for (int i = 1; i <= n; i ++) {
		for (int j = 0; j <= V; j ++) {
			f[i][j] = f[i - 1][j];
			if (j >= v[i])
				f[i][j] = min(f[i][j], f[i - 1][j - v[i]] + w[i]);
		}
	}
	int res = 0;
	for (int i = 0; i < V; i ++) {
		if (f[n][i] <= weight) res = i;
	}
	cout << res << endl;
	return 0;
}

B 修路专家 AC

sort 再逐步更新 idx,遍历一遍即可

#include <iostream>
#include <vector>
#include <algorithm>
#define aa first
#define bb second

using namespace std;
typedef pair<int, int> pii;
const int N = 1e4 + 2;
int n, L;
vector<pii> v;

int main() {
    cin >> n >> L;
    for (int i = 0; i < n; i ++) {
        int s, e; cin >> s >> e;
        v.push_back({s, e});
    }
    sort(v.begin(), v.end());
    int res = 0;
    int idx = v[0].aa;
    for (int i = 0; i < n; i ++) {
        int l = v[i].aa, r = v[i].bb;
        if (idx < l) {
            idx = l;
        } else if (idx >= r) {
            continue;
        }
        int newadd = (r - idx + L - 1) / L;
        res += newadd;
        idx += newadd * L;
    }
    cout << res << endl;
    return 0;
}

C 小小园艺师

没错,🤡又是我了,n 个节点的树,只有 n - 1 条边,俺 for 循环了 n 次😅,少了个 memset

感谢叶佬,拯救我于水火之中,这题还要特判 n == 1 的情况

从树枝开始遍历,将与树枝相连的放入队列中,当一个点的度数为零时候从队列移除

#include <iostream>
#include <cstring>
#include <queue>

using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 4e5 + 2, M = 8e5 + 2;
int idx, e[M], ne[M], h[N];
int d[N], a[N]; // 点数,被第几次删除
int n, k;

void add(int a, int b) {
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++, d[b] ++;
}

int solve() {
    cin >> n >> k;
    if (n == 1) {
    	if (k) {
    		return 0; 
    	}
    	return 1;
    }
    for (int i = 1; i < n; i ++) {
        int a, b; cin >> a >> b;
        add(a, b), add(b, a);
    }
    queue<int> q;
    for (int i = 1; i <= n; i ++) {
        if (d[i] == 1) {
            a[i] = 1;
            q.push(i);
        }
    }
    while (q.size()) {
        int t = q.front();
        q.pop();
        for (int i = h[t]; i != -1; i = ne[i]) {
            int x = e[i];
            d[x] --;
            if (d[x] == 1) {
                q.push(x);
                a[x] = a[t] + 1;
            }
        }
    }
    ll res = 0;
    for (int i = 1; i <= n; i ++) {
        if (a[i] > k) res ++;
    }
    return res;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int T; cin >> T;
    while (T --) {
        idx = 0;
        memset(h, -1, sizeof h);
        memset(d, 0, sizeof d);
        cout << solve() << endl;
    }
    return 0;
}

D 跳格子 AC

签到题🦆

#include <iostream>

using namespace std;

int main() {
    int T; cin >> T;
    while (T --) {
        int n, k; cin >> n >> k;
        if (n % k) {
            cout << 1 << endl << n << endl;
        } else {
            cout << 2 << endl << 1 << ' ' << n - 1 << endl;
        }
    }
    return 0;
}

E 狼人杀但是逆天 AC

s[i] 个人说得数小于等于 i,要使得 s[i] == n - i,若成立则 i 就是答案,若遍历后无一成立则有人分不清自己身份

#include <iostream>

using namespace std;
const int N = 102;
int n;
int a[N];

int main() {
    int T; cin >> T;
    while (T --) {
        cin >> n;
        int b[N] = {0};
        for (int i = 0; i < n; i ++) {
            cin >> a[i];
            b[a[i]] ++;
        }
        int s[N];
        s[0] = b[0];
        for (int i = 1; i <= n; i ++) s[i] = s[i - 1] + b[i];
        int res = -1;
        for (int i = 0; i <= n; i ++) {
            if (s[i] == n - i) {
                res = i;
                break;
            }
        }
        cout << res << endl;
    }
    return 0;
}

F 昊昊的难题 Hard version

状态转移表示

  • p[i] 表示前 i 个字符,以字符 p 结尾的子序列数量

  • np[i] 表示前 i 个字符,不以字符 p 结尾的子序列数量

状态计算

  • s[i] == 'p' 时,p[i] = (p[i - 1] * 2 + np[i - 1] + 1) % modnp[i] = np[i - 1]
    • 不选 s[i - 1] 时,p[i] = p[i - 1]np[i] = np[i - 1]
    • s[i] 时,p[i] = p[i - 1] + np[i - 1] + 1
  • s[i] == 'f' 时,p[i] = p[i - 1]np[i] = (np[i - 1] * 2 + 1) % mod
    • 不选 s[i - 1] 时,p[i] = p[i - 1]np[i] = np[i - 1]
    • s[i] 时,np[i] = np[i - 1] + 1
  • s[i] != 'p' && s[i] != 'f'时,p[i] = p[i - 1]np[i] = (p[i - 1] + np[i - 1] * 2 + 1) % mod
    • 不选 s[i - 1] 时,p[i] = p[i - 1]np[i] = np[i - 1]
    • s[i] 时,np[i] = p[i - 1] + np[i - 1] + 1
#include <iostream>

using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int mod = 1e9 + 7, N = 1e5 + 2;
ll p[N], np[N];

int main() {
    string s; cin >> s; 
    ll n = s.size();
    for (int i = 0; i < n; i ++) {
        if (s[i] == 'p') {
            p[i] = (p[i - 1] * 2 + np[i - 1] + 1) % mod;
            np[i] = np[i - 1];
        } else if (s[i] == 'f') {
            p[i] = p[i - 1];
            np[i] = (np[i - 1] * 2 + 1) % mod;
        } else {
            p[i] = p[i - 1];
            np[i] = (p[i - 1] + np[i - 1] * 2 + 1) % mod;
        }
    }
    cout << (p[n - 1] + np[n - 1]) % mod << endl;
    return 0;
}

G 昊昊的难题 Easy version AC

这题的子字符串需要满足连续,因此从头开始遍历,若 s[i]s[i - 1] 能构成字符串 pf,则前面(包含字符 p)有 len = i - idx 个字符,因此可以组合出 (len + 1) * len / 2 个子字符串,并且更新 idx 为字符 f 的位置 i

#include <iostream>

using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;

int main() {
    string s; cin >> s;
    int n = s.size();
    ll res = 0, ans = 0;
    int idx = 0;
    for (int i = 1; i < n; i ++) {
        if (s[i - 1] == 'p' && s[i] == 'f') {
            ll len = i - idx;
            ans = (len + 1) * len / 2;
            res = (res + ans) % mod;
            idx = i;
        }
    }
    ll len = n - idx;
            ans = (len + 1) * len / 2;
            res = (res + ans) % mod;
    cout << res << endl;
    return 0;
}

H 同构

😅,前面写的 YES|NO,样例是 Yes|No,补题时一直 WA 才注意到

这题只用暴力枚举横纵分别平移 dadb 个单位,依次匹配是否一样即可

#include <iostream>

using namespace std;

const int N = 32;
char a[N][N], b[N][N];
int n, m;

bool check(int da, int db) {
    for (int i = 0; i < n; i ++)
        for (int j = 0; j < m; j ++)
            if (a[(i + da) % n][(j + db) % m] != b[i][j])
                return false;
    return true;
}

int main() {
    cin >> n >> m;
    for (int i = 0; i < n; i ++) cin >> a[i];
    for (int i = 0; i < n; i ++) cin >> b[i];
    for (int i = 0; i < n; i ++) {
        for (int j = 0; j < n; j ++) {
            if(check(i, j)) {
                puts("YES");
                return 0;
            }
        }
    }
    puts("NO");
    return 0;
}

I 屠龙勇士 AC

先统计出每次施法最长持续时间,然后二分求得最小的时间使得满足最小伤害值

#include <iostream>

using namespace std;
typedef long long ll;
const int N = 101;
ll n, h;
ll a[N];

bool check(ll x) {
    ll res = 0;
    for (int i = 0; i < n; i ++) {
        res += min(a[i], x);
        if (res >= h) return true;
    }
    return false;
}

void solve() {
    cin >> n >> h;
    for (int i = 0; i < n; i ++) cin >> a[i];
    for (int i = 0; i < n - 1; i ++) {
        a[i] = a[i + 1] - a[i];
    }
    a[n - 1] = 1e18;
    ll l = 0, r = 1e18;
    while (l < r) {
        ll mid = (l + r) / 2;
        if (check(mid)) r = mid;
        else l = mid + 1;
    }
    cout << l << endl;
}

int main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int T; cin >> T;
    while (T --) {
        solve();
    }
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JorbanS

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值