2023CCCC选拔赛

发布时间 2023-03-26 11:26:19作者: Zeoy_kkk

7-7 与零交换

给定排列\(p:0,1,2...n-1\),每次操作你只能将一个数字和\(0\)进行交换,然后将初始排列升序排列,请你找出最少的与\(0\)交换的次数

题解:思维 + 环

  • 样例一: \(4,0,1,2,3\)
  • 我们观察位置 \(0,1,2,3,4\) 发现形成了\(0->1->2->3->4\)的环,那么交换次数就是环的长度-1,即\(n-1\)
  • 那么如果一个环中不存在\(0\),我们实际上可以先将\(0\)交换进去,答案贡献加1,然后现在环的长度+1,所以最后该环的交换次数为\(n+1\)
#include <bits/stdc++.h>
#define Zeoy std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#define debug(x) cerr << #x << '=' << x << endl
#define all(x) (x).begin(), (x).end()
#define rson id << 1 | 1
#define lson id << 1
#define int long long
#define mpk make_pair
#define endl '\n'
using namespace std;
typedef unsigned long long ULL;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-9;
const int N = 2e5 + 10, M = 4e5 + 10;

int n;
int a[N];
int vis[N];
int pos[N];

void solve()
{
    cin >> n;
    for (int i = 0; i < n; ++i)
    {
        cin >> a[i];
        pos[a[i]] = i;
    }
    int ans = 0;
    for (int i = 0; i < n; ++i)
    {
        if (!vis[a[i]])
        {
            map<int, int> mp;
            int p = a[i];
            while (!vis[p])
            {
                vis[p] = 1;
                mp[p]++;
                p = pos[p];
            }
            if (mp.count(0) && mp.size() > 1)
                ans += mp.size() - 1;
            else if (mp.size() > 1)
                ans += mp.size() + 1;
        }
    }
    cout << ans << endl;
}
signed main(void)
{
    Zeoy;
    int T = 1;
    // cin >> T;
    while (T--)
    {
        solve();
    }
    return 0;
}

7-8 出栈序列的合法性

给定一个最大容量为 M 的堆栈,将 N 个数字按 1, 2, 3, ..., N 的顺序入栈,允许按任何顺序出栈 ,给出\(q\)次询问,每次回答给定的出栈序列是否合法

题解:模拟栈 \(O(n)\)

首先出栈的顺序存在三种情况:

  1. 一入栈就出栈,例如:1 2 3
  2. 入栈几次后再出栈,例如:3 2 1
  3. 前面两种情况的复合情况

对于本题我们只需模拟栈即可,因为一个数字如果要出栈,说明这个数字之前的所有数字都已经入栈过一次了,利用该性质我们利用栈模拟该过程即可;我们发现每个数字只会被入栈和出栈依次,所以查询一次的复杂度为\(O(n)\)

#include <bits/stdc++.h>
#define Zeoy std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#define debug(x) cerr << #x << '=' << x << endl
#define all(x) (x).begin(), (x).end()
#define rson id << 1 | 1
#define lson id << 1
#define int long long
#define mpk make_pair
#define endl '\n'
using namespace std;
typedef unsigned long long ULL;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-9;
const int N = 2e5 + 10, M = 4e5 + 10;

int m, n, q;
int stk[N], tt;
int a[N];

void solve()
{
    cin >> m >> n >> q;
    while (q--)
    {
        int flag = true;
        tt = 0;
        for (int i = 1; i <= n; ++i)
            cin >> a[i];
        int pre = 1;
        for (int i = 1; i <= n; ++i)
        {
            if (tt == 0)
            {
                for (int j = 1; j <= a[i]; ++j)
                    stk[++tt] = j;
                if (tt > m)
                {
                    flag = false;
                    break;
                }
                pre = a[i];
                tt--;
            }
            else if (tt && pre < a[i])
            {
                for (int j = pre + 1; j <= a[i]; ++j)
                    stk[++tt] = j;
                if (tt > m)
                {
                    flag = false;
                    break;
                }
                pre = a[i];
                tt--;
            }
            else if (tt && pre > a[i])
            {
                if (stk[tt] != a[i])
                {
                    flag = false;
                    break;
                }
                tt--;
            }
        }
        if (flag)
            cout << "Yes" << endl;
        else
            cout << "No" << endl;
    }
}
signed main(void)
{
    Zeoy;
    int T = 1;
    // cin >> T;
    while (T--)
    {
        solve();
    }
    return 0;
}

7-12 肿瘤诊断

给定\(L\)层切片,每层切片大小为\(N×M\),体积限制为\(T\),每层切片只由0和1组成,让你数所有切片中形成的连通块中1的数量,如果该连通块中1的数量少于\(T\),则不计入答案,六个面中只要有一个面存在1就能说明两个1连通

题解:三维\(BFS\)

三维\(BFS\)板子题,当时没理解题意,没看出来是三维,裂开

#include <bits/stdc++.h>
#define Zeoy std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#define debug(x) cerr << #x << '=' << x << endl
#define all(x) (x).begin(), (x).end()
#define rson id << 1 | 1
#define lson id << 1
#define int long long
#define mpk make_pair
#define endl '\n'
using namespace std;
typedef unsigned long long ULL;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-9;
const int N = 2e5 + 10, M = 4e5 + 10;

int n, m, L, limit;
int g[65][1300][135];
bool vis[65][1300][135];
int dir[6][3] = {{0, 0, -1}, {0, 0, 1}, {0, -1, 0}, {0, 1, 0}, {1, 0, 0}, {-1, 0, 0}};
int ans;

struct node
{
    int k, x, y;
};

void bfs(int k, int x, int y)
{
    queue<node> q;
    q.push({k, x, y});
    vis[k][x][y] = true;
    int res = 0;
    while (q.size())
    {
        node u = q.front();
        q.pop();
        res++;
        for (int i = 0; i < 6; ++i)
        {
            int nk = u.k + dir[i][0];
            int nx = u.x + dir[i][1];
            int ny = u.y + dir[i][2];
            if (nx >= 1 && nx <= n && ny >= 1 && ny <= m && nk >= 1 && nk <= L && !vis[nk][nx][ny] && g[nk][nx][ny] == 1)
            {
                vis[nk][nx][ny] = true;
                q.push({nk, nx, ny});
            }
        }
    }
    if (res >= limit)
        ans += res;
}

void solve()
{
    cin >> n >> m >> L >> limit;
    for (int k = 1; k <= L; ++k)
        for (int i = 1; i <= n; ++i)
            for (int j = 1; j <= m; ++j)
                cin >> g[k][i][j];
    for (int k = 1; k <= L; ++k)
        for (int i = 1; i <= n; ++i)
            for (int j = 1; j <= m; ++j)
            {
                if (!vis[k][i][j] && g[k][i][j] == 1)
                    bfs(k, i, j);
            }
    cout << ans << endl;
}
signed main(void)
{
    Zeoy;
    int T = 1;
    // cin >> T;
    while (T--)
    {
        solve();
    }
    return 0;
}

7-13 凑零钱

一个人手里有\(10^4\)枚硬币,每种硬币只能用\(1\)次,需要请你帮她盘算一下,是否可能精确凑出要付的款额\(m\),输出字典序最小的方案

题解:贪心 + 变种\(01\)背包求方案 / \(DFS\)回溯

方法1:\(dp\)

  1. 状态表示:\(f[i]\):精准凑出金额\(i\)的钱最多需要几个硬币
  2. 状态属性:\(MAX\)
  3. 状态转移:转移的前提是前一个状态必须是合法的(也就是说前一个状态代表的金额能被硬币凑出)\(f[j] = max(f[j],f[j-a[i]]+1)\ \&\& \ (f[j-a[i]]>0\ ||\ j-a[i]==0)\)
  4. 状态初始:\(f[i]=0\)
  5. 答案呈现:如果\(f[m]>0\),代表金额\(m\)能被精准凑出,我们利用\(vector\)记录方案即可
  6. 如何求字典序最小的方案:贪心,将硬币升序排列即可,这样记录的方案一定是字典序最小的

方法2:\(dfs\)搜索

​ 贪心升序排序后\(dfs\)爆搜即可

//dp
#include <bits/stdc++.h>
#define Zeoy std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#define debug(x) cerr << #x << '=' << x << endl
#define all(x) (x).begin(), (x).end()
#define int long long
#define mpk make_pair
#define endl '\n'
using namespace std;
typedef unsigned long long ULL;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-9;
const int N = 2e5 + 10, M = 4e5 + 10;

int n, m;
int a[N];
int f[N];
vector<int> pre[N];

void solve()
{
    cin >> n >> m;
    for (int i = 1; i <= n; ++i)
        cin >> a[i];
    sort(a + 1, a + n + 1);
    for (int i = 1; i <= n; ++i)
    {
        for (int j = m; j >= a[i]; --j)
        {
            if (f[j] <= f[j - a[i]] + 1 && (f[j - a[i]] || j - a[i] == 0))
            {
                f[j] = f[j - a[i]] + 1;
                pre[j] = pre[j - a[i]];
                pre[j].push_back(a[i]);
            }
        }
    }
    if (f[m])
    {
        for (int i = 0; i < pre[m].size(); ++i)
            cout << pre[m][i] << "\n "[i < pre[m].size() - 1];
    }
    else
        cout << "No Solution" << endl;
}
signed main(void)
{
    Zeoy;
    int T = 1;
    // cin >> T;
    while (T--)
    {
        solve();
    }
    return 0;
}
//dfs回溯
#include <bits/stdc++.h>
#define Zeoy std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#define debug(x) cerr << #x << '=' << x << endl
#define all(x) (x).begin(), (x).end()
#define rson id << 1 | 1
#define lson id << 1
#define int long long
#define mpk make_pair
#define endl '\n'
using namespace std;
typedef unsigned long long ULL;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-9;
const int N = 2e5 + 10, M = 4e5 + 10;

int n, m;
int a[N];
bool vis[N];
vector<int> ans;
bool flag;

void dfs(int idx, int sum)
{
    if (sum > m)
        return;
    if (sum == m)
    {
        flag = true;
        for (int i = 0; i < ans.size(); ++i)
            cout << ans[i] << "\n "[i < ans.size() - 1];
        return;
    }
    for (int i = idx; i <= n; ++i)
    {
        if (!vis[i])
        {
            vis[i] = true;
            ans.push_back(a[i]);
            dfs(i + 1, sum + a[i]);
            ans.pop_back();
            vis[i] = false;
            if (flag)
                return;
        }
    }
}

void solve()
{
    cin >> n >> m;
    int sum = 0;
    for (int i = 1; i <= n; ++i)
        cin >> a[i], sum += a[i];
    if (sum < m)
    {
        cout << "No Solution" << endl;
        return;
    }
    sort(a + 1, a + n + 1);
    dfs(1, 0);
    if (!flag)
        cout << "No Solution" << endl;
}
signed main(void)
{
    Zeoy;
    int T = 1;
    // cin >> T;
    while (T--)
    {
        solve();
    }
    return 0;
}