2021牛客OI赛前集训营-J组-2 解析
A 恰饭
题干:
牛牛来到一家餐馆恰饭。菜单里有 道菜,价格分别为 元,还有 道甜品,价格分别为 元。牛牛需要点一道菜和一道甜品,但牛牛又不想花费太多钱,请你帮帮他找出最小需要花费多少元。
解析:
这道题简直不要太难了。博主花了三个半小时零一分钟才做出来
额这题的话应该不用写解析了吧。直接上代码:
答案:
/*
 * @Link: https://ac.nowcoder.com/acm/contest/20101/A
 * @Date: 2021-10-06 18:33:59
 * @LastEditTime: 2021-10-06 18:37:36
 * @FilePath: \牛客\2021牛客OI赛前集训营-J\第二场\A 恰饭\eat.cpp
 * @Method: 
 */
#include <iostream>
int x = 1001, y = 1001;
int main()
{
    for (int i = 1, a; i <= 4; i++)
        scanf("%d", &a), x = a < x ? a : x;
    for (int i = 1, b; i <= 3; i++)
        scanf("%d", &b), y = b < y ? b : y;
    printf("%d", x + y);
    return 0;
}
B 卡片
题干:
牛牛有 张卡片,第 张卡片上有一个数字 。牛牛在里面选出了 张,按照某种顺序依次排列成一个数。
比如牛牛选出了 这三张卡片,牛牛就可以排列成 这五个数。 你需要帮牛牛求出对于所有选出 张卡片的方案,牛牛总共能拼成多少种不同的数字。
解析:
显然这题要用搜索,属于比较入门的搜索题。我想学过的应该都做过类似的题吧。
答案1(AC, 6ms~9ms):
/*
 * @Link: https://ac.nowcoder.com/acm/contest/20101/B
 * @Date: 2021-10-06 18:40:30
 * @LastEditTime: 2021-10-06 19:26:00
 * @FilePath: \牛客\2021牛客OI赛前集训营-J\第二场\B 卡片\card.cpp
 * @Method: DFS
 */
#include <iostream>
#include <cmath>
#include <vector>
using namespace std;
const int N = 19;
int n, k, card[N], cnt;
bool used[N];
vector <int> vec;
bool check(int x) // * pass
{
    for (auto i = vec.begin(); i != vec.end(); i++)
        if (*i == x)
            return true;
    return false;
}
int getDigit(int x) // * pass
{
    int ans = 0;
    while (x)
        ans++, x /= 10;
    return ans;
}
void dfs(int times, int cur)
{
    if (times == k)
    {
        // printf("%d\n", cur); //? test
        if (!check(cur))
            cnt++, vec.push_back(cur);
        return ;
    }
    for (int i = 1; i <= n; i++)
    {
        if (used[i])
            continue;
        used[i] = true;
        int temp = cur * pow(10, getDigit(card[i])) + card[i];
        // printf("%d ", temp); //? test
        dfs(times+1, temp);
        used[i] = false;
    }
}
int main()
{
    scanf("%d%d", &n, &k);
    for (int i = 1; i <= n; i++)
        scanf("%d", card + i);
    dfs(0, 0);
    printf("%d", cnt);
    return 0;
}
答案2(AC, 3ms~5ms, unordered_map+字符串优化):
/*
 * @Link: https://ac.nowcoder.com/acm/contest/20101/B
 * @Date: 2021-10-06 18:40:30
 * @LastEditTime: 2021-10-07 20:50:59
 * @FilePath: \Hydro题库d:\WillCpp\牛客\2021牛客OI赛前集训营-J\第二场\B 卡片\card_AC.cpp
 * @Method: DFS
 */
#include <iostream>
#include <string>
#include <unordered_map>
std::unordered_map <std::string, bool> mp;
std::string a[17];
int cnt, n, k;
bool vis[17];
void dfs(int x, std::string s)
{
    if (x > k)
    {
        if (mp[s])
            return ;
        mp[s] = true, ++cnt;
        return ;
    }
    for (int i = 1; i <= n; i++)
    {
        if (vis[i])
            continue;
        vis[i] = true;
        dfs(x+1, s+a[i]);
        vis[i] = false;
    }
}
int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(0); std::cout.tie(0);
    std::cin >> n >> k;
    for (int i = 1; i <= n; i++)
        std::cin >> a[i];
    dfs(1, "");
    std::cout << cnt;
    return 0;
}
C 数数
题干:
我们称一个集合 是好的,当且仅当 或把它们按照 降序排序后满足:
对于所有满足 的 ,有 或者 。
牛牛在二维平面上有一个 个点的集合。牛牛请你帮他算算有多少个非空子集 是好的。因为答案可能很大,你只需要告诉他答案对 取模后的结果。
解析:
这道题属于计数问题。一般我们应对计数问题,解决办法分为三类:
- 枚举/搜索
 - 通过组合数学(加法原理,乘法原理)
 - 通过动态规划
 
尝试前两者后,可以发现并不好做。所以这道题我们选择用动态规划。
通过将题目中的信息作图,我们可以绘出以下两张图:
第一种合法情况:
第二种合法情况:
考虑动规,我们又想到两种方式:
A. 按峰谷
B. 按端点
前者的状态转移方程不太好推,所以我们选择后者。
定状态:
f[i][0] 代表  作为右端点的所有子集
f[i][1] 代表  作为左端点的所有子集
状态转移:
if (y[j] > y[i])
    f[j][1] += f[i][0]
else
    f[i][0] += f[j][1]
答案:
/*
 * @Link: https://ac.nowcoder.com/acm/contest/20101/C
 * @Date: 2021-10-06 19:29:06
 * @LastEditTime: 2021-10-08 21:35:35
 * @FilePath: \牛客\2021牛客OI赛前集训营-J\第二场\C 数数\count_AC.cpp
 * @Method: DP
 */
#include <iostream>
#include <algorithm>
#define x first
#define y second
const int N = 6005;
const int mod = 1e9 + 7;
std::pair <int, int> p[N];
int n, dp[N][2], ans = 0;
int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
        scanf("%d%d", &p[i].x, &p[i].y);
    std::sort(p + 1, p + n + 1);
    for (int i = 1; i <= n; i++) {
        dp[i][0] = dp[i][1] = 1;
        for (int j = i - 1; j >= 1; j--) {
            if (p[j].y > p[i].y) (dp[j][1] += dp[i][0]) %= mod;
            else (dp[i][0] += dp[j][1]) %= mod;
        }
    }
    ans = mod - n;
    for (int i = 1; i <= n; i++)
        ans = ((ans + dp[i][0]) % mod + dp[i][1]) % mod;
    printf("%d\n", ans);
    return 0;
}
D 划分
题干:
牛牛有一棵 个节点的有根树,节点编号为 到 ,根节点为 。节点 上写有数字 。
我们称一条直链为一条 到 的路径,其中 是 的祖先或 (注意:这里的直链和链的定义不同)。
牛牛想要将这棵树划分成若干直链,满足每个节点恰好属于一条直链,如果对于划分出的每条直链,将该链上的点上写的数字任意排列,最后的结果满足对于任意节点 ,节点 上写的数字为 ,那我们就称这种划分方案是好的。
你需要回答牛牛是否存在好的划分方案。
解析:
一道搜索题。dfs 搜他!
注意:
- 若某点的配对点不在其至根的链上 No
 - 若某点的查找过程中遇到了别的直链 No
 
答案:
/*
 * @Link: https://ac.nowcoder.com/acm/contest/20101/D
 * @Date: 2021-10-07 21:27:09
 * @LastEditTime: 2021-10-08 21:44:32
 * @FilePath: \牛客\2021牛客OI赛前集训营-J\第二场\D 划分\partition_AC.cpp
 * @Method: DFS
 */
#include <iostream>
#include <cstring>
const int N = 1e5 + 9;
int t, n, m, i, j, k, a, b, bad;
int x[N], y[N], v[N], h[N], f[N];
struct Node {
    int a, b, n;
} d[N << 1];
void cun(int a, int b) {
    d[++k].a = a, d[k].b = b;
    d[k].n = h[a], h[a] = k;
}
void dfs(int a, int p) {
    int b, s, t;
    if (bad)
        return;
    for (int i = h[a]; i; i = d[i].n) {
        b = d[i].b;
        if (b == p)
            continue;
        dfs(b, f[b] = a);
    }
    if (x[a] != y[a]) {
        if (!v[a])
            v[a] = ++k;
        s = a;
        while (t = f[s]) {
            if (v[t] && v[t] != v[a])
                bad = 1;
            v[t] = v[a];
            if (x[t] == y[a]) {
                std::swap(x[t], x[a]);
                break;
            } else
                s = t;
        }
        if (!t)
            bad = 1;
    }
}
int main() {
    scanf("%d", &t);
    while (t--) {
        scanf("%d", &n);
        memset(h, 0, sizeof(h));
        memset(f, 0, sizeof(f));
        memset(v, 0, sizeof(v));
        for (i = k = 1; i < n; i++) {
            scanf("%d%d", &a, &b);
            cun(a, b);
            cun(b, a);
        }
        for (i = 1; i <= n; i++) {
            scanf("%d", &x[i]);
        }
        for (i = 1; i <= n; i++) {
            scanf("%d", &y[i]);
        }
        bad = 0;
        dfs(k = 1, 0);
        printf("%s\n", bad ? "No" : "Yes");
    }
    return 0;
}
更新日志
UPT 2022/06/03: 修正了一处格式错误

京公网安备 11010502036488号