2019 ICPC 银川题解(A,H,L)

news2024/10/7 8:27:37

赛时没发挥好6题金尾(rank38),剩下很多能写的题,其中四个dp,傻眼ing
The 2019 ICPC Asia Yinchuan Regional Contest

A Girls Band Party(背包)

有点迷惑的题,当时看只要 5 5 5 张牌一下子想到暴力枚举,结果发现是不太能行的,导致浪费很多时间。

题意

n n n 张牌,每张牌有 名称,颜色,价值。再给定 5 5 5 个特殊的牌的名称,和一个特殊的牌的颜色。要求从 n n n 张牌中选出 5 5 5 张名称不同的卡牌,基础分数为 5 5 5 张牌的价值之和 s u m sum sum,在此基础上每多一张牌是特殊颜色最终价值加上 0.2 ∗ s u m 0.2*sum 0.2sum,每多一张特殊名字牌最终价值加上 0.1 ∗ s u m 0.1*sum 0.1sum,最终价值向下取整,求选出的卡牌最大可能的价值。

思路

对于复杂的题目我们先从简单的问题开始考虑。

若是对于该问题只考虑价值之和不考虑附加价值和不能同名的问题是否能解决?就是一个简单的背包,容量为 5 5 5 求最大值的问题。

接下来增加新的信息,若是要求 5 5 5 张牌不能重名如何解决?也很简单,分组背包,同名的牌视作一组,一组内的物品只能选一样的背包问题

再增加新的信息,普通颜色和普通名称对答案没有任何影响,特殊颜色和名称也只有数量对答案有影响,和具体是什么无关。我们是否能在背包时顺便统计特殊颜色和特殊名称?,也很简单,因为只要选出 5 5 5 张牌,将特殊颜色和特殊名称也当做价值的一种算进dp方程,给两个各多开一维维护即可。

到此问题已经顺利解决,给出dp方程: d p [ i ] [ j ] [ k ] [ p ] : dp[i][j][k][p]: dp[i][j][k][p] i i i 张卡牌,选出 j j j 张卡牌, k k k 张是特殊颜色, p p p 张是特殊名字卡牌的 合法方案中的最大基数价值。

具体实现可以看代码,有注释和解析。

#include <bits/stdc++.h>
using namespace std;

#define ll long long

const int N = 1e5 + 10;

int n;

string s[10], color;
map<string, bool> mp;

struct node{
    string d, c;
    int val;
}cd[N];

bool cmp(node& A, node& B){
    return A.d < B.d;
}

int f[N][6][6][6]; // 前 i 张卡牌,选出 j 张卡牌,k 张是特殊颜色,p 张是特殊名字卡牌的 最大基数价值

void cmax(int& a, int b){ a = max(a, b); }
void solve(){
    int n;
    cin >> n;
    for(int i = 1; i <= n; i ++){
        cin >> cd[i].d >> cd[i].c >> cd[i].val;
    }

    mp.clear();
    for(int i = 0; i < 5; i ++){
        cin >> s[i];
        mp[s[i]] = 1;
    }
    cin >> color;

    sort(cd + 1, cd + 1 + n, cmp);  // 按名称排序,方便进行类似分组背包的dp
    for(int i = 1; i <= n; i ++){
        for(int j = 0; j < 6; j ++){
            for(int k = 0; k < 6; k ++){
                for(int p = 0; p < 6; p ++) f[i][j][k][p] = 0;
            }
        }
    }

    int last = 0; // 记录最近的上一个不同名的卡牌为止
    for(int i = 1; i <= n; i ++){
        if(cd[i].d != cd[i - 1].d) last = i - 1; 

        int c_v = (cd[i].c == color), s_v = mp.count(cd[i].d); // 这张牌是否是特殊颜色和名称

        for(int j = 0; j <= 5; j ++){
            for(int k = 0; k <= j; k ++){
                for(int p = 0; p <= j; p ++){
                    
                    // 不取这张牌
                    cmax(f[i][j][k][p], f[i - 1][j][k][p]); // 可以从同名的卡牌转移 也可以从不同名的转移

                    // 取这张牌
                    if((j != 0 && f[last][j][k][p] == 0) || j == 5) continue ; // j = 5 时注意不要越界了
                    cmax(f[i][j + 1][k + c_v][p + s_v], f[last][j][k][p] + cd[i].val); // 取这一张牌,那么只能从不同名的卡牌转移
                }
            }
        }
    }
   
    ll ans = 0;
    for(int i = 0; i <= 5; i ++){ // 枚举特殊颜色数量
        for(int j = 0; j <= 5; j ++){ // 枚举特殊名字数量
            double mul = 0.2 * (double)i + 0.1 * (double)j; // 系数
            ans = max(ans, (ll)(mul * (double)f[n][5][i][j]) + f[n][5][i][j]);
        }
    }
    cout << ans << "\n";
}

int main(){ 
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);

    int t;
    cin >> t;
    while(t --){
        solve();
    }
    return 0;
}

H Delivery Route(最短路+拓扑排序)

题意

给定一个有向图 G G G x x x 条双向边 y y y 条单向边,可能有负边(保证是单向边)但保证走过单向边 u → v u\rightarrow v uv 后一定走不回 u u u,求起点 s s s 到所有点的最短路,如果到不了输出 “NO PATH”.

思路

乍一看仿佛很简单,跑个迪杰斯特拉(后文简称djs)最短路不就行了吗?但是写完准备交的时候就发现有坑,因为djs算法是每次从优先队列取出目前为止到起点最近的点 u u u 进行扩展,但是这样的扩展不一定是最优的,但之前扩展过一次下次再想用该点 u u u 扩展就不行了。
考虑这样一个图: 1 1 1 是起点
在这里插入图片描述

1 → 2 → 4 → 5 1\rightarrow2\rightarrow4\rightarrow5 1245 显然是第一轮扩展,在这几个点取出队列之前队首都不会轮到 3 3 3,因为此时 d i s [ 3 ] = 2 > d i s [ 2 ] = 1 > d i s [ 4 ] = 0 > d i s [ 5 ] = 1 dis[3] = 2 > dis[2] = 1 > dis[4] = 0 > dis[5] = 1 dis[3]=2>dis[2]=1>dis[4]=0>dis[5]=1,因为此时这几个点都已经作为过一次扩展点了,之后就不能再次扩展,但是发现当最后点 3 3 3 进行扩展时, 3 → 4 → 5 3\rightarrow4\rightarrow5 345 会更优秀。

由于题目保证没有负环,我们考虑使用拓扑排序进行优化,先将双向边连接起的连通块用并查集合并,再将单向边连接的这些连通块点集度数 + 1 + 1 +1但要注意若是有单向边 u → v u\rightarrow v uv,且 u u u 点是不可从起点到达的,就不用记录该度数。

接下来就是普通的djs算法 + 拓扑排序了,只有当度数为 0 0 0 时才将一个连通块加入优先队列。具体见代码和注释。

#include <bits/stdc++.h>
using namespace std;

#define ll long long
typedef pair<ll, int> pli;

const int N = 25010;
const ll inf = 1e18;

vector<pair<int, int> > g[N], e[N];

int n, m1, m2, s;

priority_queue<pli, vector<pli>, greater<pli>> q;

ll dis[N];
bool vis[N];

struct DSU {
    std::vector<int> f, siz;
    
    DSU() {}
    DSU(int maxn) {
        init(maxn);
    }
     
    void init(int maxn) {
        f.resize(++ maxn); // 重构容器大小到 n
        std::iota(f.begin(), f.end(), 0); // 批量递增赋值
        // siz.assign(maxn, 1); // 赋值n个1
    }
     
    int find(int x) {
        while (x != f[x]) {
            x = f[x] = f[f[x]];
        }
        return x;
    }
     
    bool same(int x, int y) {
        return find(x) == find(y);
    }
     
    bool merge(int x, int y) {
        x = find(x);
        y = find(y);
        if (x == y) {
            return false;
        }
        f[y] = x;
        return true;
    }
};


DSU dsu;
int d[N];
vector<int> ID[N];
void djs_top(){
    for(int i = 1; i <= n; i ++) dis[i] = inf, vis[i] = 0;
    dis[s] = 0;

    q.push({0, s});
    while(!q.empty()){
        auto [di, u] = q.top(); q.pop();

        if(vis[u]) continue ;
        vis[u] = 1;
        // djs
        for(auto [v, w] : g[u]){
            if(dis[v] > dis[u] + w){
                dis[v] = dis[u] + w;
                q.push({dis[v], v});
            }
        }

        // 拓扑排序
        for(auto [v, w] : e[u]){ // 单向边
            int id = dsu.find(v);
            d[id] --;
            if(dis[v] > dis[u] + w) dis[v] = dis[u] + w;
            ID[id].push_back(v); // 存入连通块
            if(!d[id]){ // 当连通块度数为0,再将所有块内之前出现过的点存入队列
                for(auto x : ID[id]) q.push({dis[x], x});
            }
        }
    }
}

void dfs(int u){
    if(vis[u]) return ;
    vis[u] = 1;
    for(auto [v, w] : g[u]) dfs(v);
    for(auto [v, w] : e[u]) dfs(v);
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);

    cin >> n >> m1 >> m2 >> s;

    dsu.init(n);
    for(int i = 1; i <= m1; i ++){
        int u, v, w;
        cin >> u >> v >> w;
        dsu.merge(u, v); // 并查集合并
        g[u].push_back({v, w});
        g[v].push_back({u, w});
    }

    for(int i = 1; i <= m2; i ++){
        int u, v, w;
        cin >> u >> v >> w;
        e[u].push_back({v, w});
    }
    dfs(s); // 将不可达的点判除

    for(int i = 1; i <= n; i ++){
        if(!vis[i]) continue ;
        for(auto [v, w] : e[i]) d[dsu.find(v)] ++; // 连通块的度数 ++
    }
    djs_top();

    for(int i = 1; i <= n; i ++){
        if(!vis[i]) cout << "NO PATH\n";
        else cout << dis[i] << "\n";
    }
    return 0;
}

L Xian Xiang

思路简单,处理很麻烦的一道题,差点做吐了。
在这里插入图片描述

题意

每次给定 n ∗ m n*m nm 的矩阵,每个单位代表一个对象(以长度为 k k k 的字符串的形式给出),每次可以删除两个对象(类似连连看,但要求连线必须水平或垂直且最多只能改变一次方向)且路径上不能有未删除的对象(可以有空对象)。
删除两个对象的价值为对应位置上字符相同的数目对应的价值,若相同字符数目为 i i i,价值为 s i s_i si. 题目保证非空对象最多为 18 18 18 个,且为偶数,求可能删除方案的最大价值。

思路

很简单就能想到状压dp,每次枚举两个未删除的非空对象判断是否能连线删除即可,能删除就能转移。难的是处理起来很麻烦,并且题目有 T T T 组样例,要求转移的判断常数要比较小才行。

设检查函数的时间复杂度为 O ( k ) O(k) O(k),状压复杂度 2 18 2^{18} 218,但删除必然是两个一删除,降一阶为 2 17 2^{17} 217,每次需要枚举未删除的两个数,所以总复杂度为 O ( 3407872 ∗ T ∗ k ) O(3407872*T*k) O(3407872Tk).

计算复杂度代码:

int main(){
	int cnt = 0;
	for(int i = 0; i < (1 << 18); i ++){ // 状压
		int x = 0; // 剩余对象的数量
		for(int j = 0; j < 18; j ++){
			if(!(i >> j & 1)) x ++;
		}
		if(x & 1) continue ; // 肯定为偶数
		cnt += x * (x + 1) / 2	;
	}
	cout << cnt;
	return 0;
}

我自己代码经过大量预处理,使得 O ( k ) O(k) O(k) 的复杂度降低为当前剩余对象的数量,得以通过此题。具体见代码和注释。

#include <bits/stdc++.h>
using namespace std;

typedef vector<int> Vec;
typedef pair<int, int> pii;

const int N = (1 << 19), M = 20;

int t, n, m, k;
pii id[M]; // 编号为 i 的对象的坐标
int cr[M][M], val[M], v[M][M]; // cr:坐标为i, j的对象的编号, val:价值, v:编号为i和j的对象一起删除的价值
string s[M][M]; // 对象

int get_id(int x, int y){
    return cr[x][y];
}

int f[N]; // 删除状态表示的对象最大价值
// int cmax(int& a, int b){ a = max(a, b); }

bool mp[M][M][M][2]; // 从 i 号到 j 号的两条路径上是否存在 k 号

void checkl(int id1, int id2){
    auto [x1, y1] = id[id1]; 
    auto [x2, y2] = id[id2];

    // 先向下再向左
    for(int i = x1; i <= x2; i ++){
        if(s[i][y1][0] != '-') mp[id1][id2][cr[i][y1]][0] = true;
    }
    for(int j = y1; j >= y2; j --){
        if(s[x2][j][0] != '-') mp[id1][id2][cr[x2][j]][0] = true;
    }

    // 先向左再向下
    for(int j = y1; j >= y2; j --){
        if(s[x1][j][0] != '-') mp[id1][id2][cr[x1][j]][1] = true;
    }
    for(int i = x1; i <= x2; i ++){
        if(s[i][y2][0] != '-') mp[id1][id2][cr[i][y2]][1] = true;
    }

    // 将自己编号记为0
    mp[id1][id2][id1][0] = mp[id1][id2][id2][0] = 0;
    mp[id1][id2][id1][1] = mp[id1][id2][id2][1] = 0;
}

void checkr(int id1, int id2){
    auto [x1, y1] = id[id1]; 
    auto [x2, y2] = id[id2];

    // 先下再右
    for(int i = x1; i <= x2; i ++){
        if(s[i][y1][0] != '-') mp[id1][id2][cr[i][y1]][0] = true;
    }
    for(int j = y1; j <= y2; j ++){
        if(s[x2][j][0] != '-') mp[id1][id2][cr[x2][j]][0] = true;
    }

    // 先右再下
    for(int j = y1 + 1; j <= y2; j ++){
        if(s[x1][j][0] != '-') mp[id1][id2][cr[x1][j]][1] = true;
    }
    for(int i = x1 + 1; i <= x2; i ++){
        if(s[i][y2][0] != '-') mp[id1][id2][cr[i][y2]][1] = true;
    }   
    // 同理
    mp[id1][id2][id1][0] = mp[id1][id2][id2][0] = 0;
    mp[id1][id2][id1][1] = mp[id1][id2][id2][1] = 0;
}

void init(){
    memset(mp, 0, sizeof mp);
    for(int i = 0; i < t; i ++){
        for(int j = i + 1; j < t; j ++){
            int sum = 0;
            auto [x1, y1] = id[i];
            auto [x2, y2] = id[j];
            for(int r = 0; r < k; r ++) sum += (s[x1][y1][r] == s[x2][y2][r]); 
            v[i][j] = val[sum]; // 计算价值
            if(y2 <= y1) checkl(i, j); // 因为编号大的肯定在下方,但不一定是左边还是右边,分左右分别记录路径
            else checkr(i, j);
        }
    }
    for(int i = 1; i < (1 << t); i ++) f[i] = -1;
}

bool check(int id1, int id2, Vec& vis){
    int f[] = {1, 1, 1};
    for(auto x : vis){ // 查询是否两条路径上都有点未删除
        if(mp[id1][id2][x][0]) f[0] = 0;
        if(mp[id1][id2][x][1]) f[1] = 0;
        if(f[0] == 0 && f[1] == 0) return false;
    }
    return true;
}

void solve(){
    cin >> n >> m >> k;
    t = 0;
    for(int i = 0; i < n; i ++){
        for(int j = 0; j < m; j ++){
            cin >> s[i][j];
            if(s[i][j][0] != '-'){
                cr[i][j] = t;
                id[t] = {i, j};
                t ++; // 给每个非空对象编号
            }
        }
    }
    for(int i = 0; i <= k; i ++) cin >> val[i];

    init();

    for(int i = 0; i < (1 << t); i ++){
        if(f[i] == -1) continue ;
        Vec r, g;
        for(int j = 0; j < t; j ++){
            if(!(i >> j & 1)) g.push_back(j); // 记录未删除的非空对象
        }

        int siz = g.size();
        for(int j = 0; j < siz; j ++){
            for(int p = j + 1; p < siz; p ++){ // 枚举两个未删除的对象
                if(check(g[j], g[p], g)){ // 未删除,check是否能连线
                    f[i | (1 << g[j]) | (1 << g[p])] = max(f[i | (1 << g[j]) | (1 << g[p])], f[i] + v[g[j]][g[p]]); // 转移
                    // cmax(f[i | (1 << j) | (1 << p)], f[i] + d[j][p]); // RE
                }
            }
        }
    }

    cout << f[(1 << t) - 1] << "\n";
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);

    int T;
    cin >> T;
    while(T --){
        solve();
    }
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1182511.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

400 页共计 800 道软件测试面试真题汇总,2023年吐血整理

800 道软件测试面试真题&#xff0c;高清打印版打包带走&#xff0c;横扫软件测试面试高频问题&#xff0c;涵盖测试理论、Linux、MySQL、Web 测试、接口测试、App 测试、Python、Selenium、性能测试、LordRunner、计算机网络、数据结构与算法、逻辑思维、人力资源等模块面试题…

python对Windows如何进行关机/重启?

用CMD命令进行关机/重启步骤&#xff1a; 1.winR&#xff0c;换出输入框 2.在输入框输入命令&#xff0c;如关机&#xff1a;shutdown -s -t 20&#xff0c;该命令是20秒后关机。 命令说明 -s 关机 -r 重启 -t 时间&#xff0c;后面是数字是你要设置的秒数 -a 取消命令&…

lv11 嵌入式开发 计算机硬件基础 1

目录 1 导学 1.1回顾及导学 1.2 嵌入式系统分层 1.3 linux底层开发 2 ARM体系结构与接口技术课程导学 3 计算机基础 3.1 计算机的进制 3.2 计算机组成 3.3 总线 4 多级存储结构与地址空间 4.1 多级存储概念 4.2 地址空间 5 CPU工作原理 6 练习 1 导学 1.1回顾及导…

记CVE-2022-39227-Python-JWT漏洞

文章目录 前言影响版本漏洞分析Newstar2023 Week5总结 前言 在Asal1n师傅的随口一说之下&#xff0c;说newstar week5出了一道祥云杯一样的CVE&#xff0c;于是自己也是跑去看了一下&#xff0c;确实是自己不知道的一个CVE漏洞&#xff0c;于是就从这道题学习到了python-jwt库…

【算法 | 模拟No.3】leetcode 38. 外观数列

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【手撕算法系列专栏】【Leetcode】 &#x1f354;本专栏旨在提高自己算法能力的同时&#xff0c;记录一下自己的学习过程&#xff0c;希望…

threejs BufferGeometry更新了顶点后,可能导致部分位置拾取失效

产生现象的操作&#xff1a; 通过点击线上的点&#xff0c;去更新线的BufferGeometry&#xff0c;导致&#xff0c;只能在更新顶点坐标之前的线的区域上才能被拾取到 解决办法 mesh.geometry.computeBoundingSphere();

Vue - Syntax Error: TypeError: this.getOptions is not a function 项目运行时报错,详细解决方案

报错问题 关于此问题网上的教程都无法解决,如果您的报错与本文相似,本文即可 100% 完美解决。 在 vue2.js 项目中,执行 npm run serve 运行时出现如下报错信息, Syntax Error: TypeError: this.getOptions is not a function 解决方案 按照以下步骤,即可完美解决。 这个错…

9.斐波那契数列

斐波那契数列&#xff08;Fibonacci sequence&#xff09;&#xff0c;也称之为黄金分割数列&#xff0c;由意大利数学家列昂纳多・斐波那契&#xff08;Leonardo Fibonacci&#xff09;提出。斐波那契数列指的是这样的一个数列&#xff1a;1、1、2、3、5、8、13、21、34、………

基于Qt QProcess获取linux启动的程序、QScreen 截屏、GIF动画实现

在Linux中,可以使用QProcess类来获取已启动的程序。以下是一个示例代码: #include <QCoreApplication>#include <QProcess>int main(int argc, char *argv[]){QCoreApplication a(argc, argv); // 创建一个QProcess对象 QProcess process; // 设置执行…

关系数据理论 规范化

码&#xff08;Key&#xff09; 候选码&#xff1a;某一属性组的值可唯一标识一个元组&#xff0c;其子集不能&#xff0c;该属性组为候选码&#xff08;如学生表中的学号&#xff0c;成绩表中学号课程号&#xff09; R<U,F> K是R的候选码 主属性&#xff1a;候选…

什么GAN生成对抗网络?生成对抗网络可以干什么?

生成对抗网络(Generative Adversarial Nets,简称GAN)。神经网络分很多种,有普通的前向传播网络,有分析图片的CNN卷积神经网络,有分析系列化数据比如语言、文字的RNN循环神经网络,这些神经网络都是用来输入数据,得到想要的结果,我们看中的是这些神经网络中很好地将数据与…

Python 机器学习入门:数据集、数据类型和统计学

机器学习是通过研究数据和统计信息使计算机学习的过程。机器学习是迈向人工智能&#xff08;AI&#xff09;的一步。机器学习是一个分析数据并学会预测结果的程序。 数据集 在计算机的思维中&#xff0c;数据集是任何数据的集合。它可以是从数组到完整数据库的任何东西。 数…

3线SPI驱动 HX8347 TFT屏

老五家2.8寸屏&#xff0c;3线SPI驱动 前言 要知道屏幕的驱动芯片都小的惊人&#xff0c;想必是不会打上丝印的。从几百个引脚中判断哪个是哪个&#xff0c;想想就晕。 大佬们都太厉害了&#xff0c;看看PFC就知道屏幕的接线定义。一直好奇这种神技是怎么练成的。也尝试自己来…

Excel宏标记在所有工作表中标记关键字(以域名为例)并将结果输出到另一张Sheet

Excel宏标记在所有工作表中标记关键字(以域名为例)并将结果输出到另一张Sheet 因为我的需求是标记一组url&#xff0c;所以使用正则进行匹配&#xff0c;将匹配到的url标红&#xff0c;并将标记结果统计输出到新建的名为“标记结果”的Sheet中 效果如下&#xff1a; 统计页 …

jeecgboot vue3使用JAreaSelect地区选择组件时返回省市区的编码,如何获取到选择地区的文字

JAreaSelect文档地址&#xff1a;添加链接描述 当我们的BasicForm表单组件中使用选择省市区的JAreaSelect组件时&#xff0c;获取到的返回值是地区的编码&#xff0c;如“530304”这样子&#xff0c;但我在小程序中展示数据的时候需要明确的地址&#xff0c;如“云南省昆明市五…

WordPress主题 JustNews主题6.0.1(亲测首页不空白)

介绍 资源入口 需要用WordPress5.X版本 JustNews介绍&#xff1a;一款专为博客、自媒体、资讯类的网站设计开发的WordPress主题&#xff0c;自v3.0版开始支持自主研发的前端用户中心&#xff0c;不仅支持注册、登录、账户设置、个人中心等常用页面的添加&#xff0c;还可以上传…

wandb报错Network error (ProxyError), entering retry loop

解决方案&#xff1a;改成离线模式 import os import wandb os.environ[“WANDB_API_KEY”] ‘KEY’ os.environ[“WANDB_MODE”] “offline” 原因&#xff1a; 使用wandb在线模式运行代码&#xff0c;服务器是一边运行我们的代码一边向wandb官网上传我们的数据&#xff0…

如何使用Pyarmor保护你的Python脚本

目录 一、Pyarmor简介 二、使用Pyarmor保护Python脚本 1、安装Pyarmor 2、创建Pyarmor项目 3、添加Python脚本 4、配置执行环境 5、生成保护后的脚本 三、注意事项与未来发展 四、未来发展 五、总结 本文深入探讨了如何使用Pyarmor工具保护Python脚本。Pyarmor是一个…

Python和BeautifulSoup库的魔力:解析TikTok视频页面

概述 短视频平台如TikTok已成为信息传播和电商推广的重要渠道。用户通过短视频分享生活、创作内容&#xff0c;吸引了数以亿计的观众&#xff0c;为企业和创作者提供了广阔的市场和宣传机会。然而&#xff0c;要深入了解TikTok上的视频内容以及用户互动情况&#xff0c;需要借…

SpringSecurity6 | 委派筛选器代理和过滤器链代理

✅作者简介&#xff1a;大家好&#xff0c;我是Leo&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;Leo的博客 &#x1f49e;当前专栏&#xff1a; Java从入门到精通 ✨特色专栏&#xf…